Linux网络编程
第一章:网络基础
1. 协议
1.1 什么是协议?
从应用的角度出发,协议可以理解为“规则”,是数据传输和数据的解释规则。
假设,A与B双方欲传输文件,规定:
第一次,传输文件名,接收方接收到文件名,应答OK给传输方
第二次,发送文件的尺寸,接收方接收到该数据再次应答一个OK
第三次,传输文件内容。同样,接收方接收数据完成后应答OK表示文件内容接收成功。
这种仅在A、B之间被遵守的协议称之为原始协议、当次协议被更多的人采用,不断地增加、改进、维护和完善。最终形成一个稳定的、完整的文件传输协议,被广泛应用于各种文件传输过程中。该协议就成为一个标准协议。最早的ftp协议就是由此衍生而来。
1.2 典型协议
应用层:HTTP协议、FTP协议
传输层:TCP/UDP协议
网络层:IP协议、ICMP协议、IGMP协议
网络接口层:ARP协议、RARP协议
2. OSI7层模型和TCP/IP4层模型
TCP/IP常用协议:
- 应用层:http、ftp、nfs、ssh、telnet......
- 传输层:TCP、UCP
- 网络层:IP、ICMP、IGMP
- 网络接口(链路层):以太网帧协议、ARP协议
3. 网络通信过程
系统帮助我们把数据一步步封装,交给网卡,网卡接收到封装的数据,在网络上传输到目的主机的网卡,目的主机的网卡再一步步解封。
数据没有封装就不能在网络中传递!!!好比寄快递,不添加地址姓名电话等相关信息,接收人就无法接收到快递。
3.1 以太网帧和ARP请求
以太网帧的格式:
由于本主机不知道目的主机的MAC地址,因此在给目的主机发送以太网帧之前,先通过ARP请求来获取目的主机的MAC地址。
路由器接收到ARP请求之后,经过查找和广播等多次寻找,最终发出ARP应答给本主机。
ARP协议:根据IP地址获取MAC地址(网卡唯一标识)
以太网帧协议:根据MAC地址,完成数据传输。
3.2 IP协议
版本:IPv4,IPv6
TTL:time to live。设置数据包在路由节点中的跳转上限。每经过一个路由节点,该值减1.
IP地址:可以在网络环境中,唯一标识一台主机
端口号:可以在网络的一台主机上,唯一标识一个进程
IP地址+端口号:可以在网络环境中,唯一标识一个进程。
3.3 TCP协议
3.4 C/S和B/S模型
Client-Server | Browse-Server | |
优点 | 缓存大量数据、协议选择灵活 速度快 | 安全性好、跨平台、开发工作量较小 |
缺点 | 安全性差、开发量大(客户端服务器都要开发) | 不能缓存大量数据、严格遵守http |
第二章:Socket编程
1. 字节序
对于整数0x12345678,我们将这个4字节整数从低地址往高地址存。
0x12是最高位,0x78是最低位。
大端存储最先存最高位。
小端存储最先存最地位。
TCP/IP协议规定:网络数据流应采用大端字节序。而大部分主机字节序时小端字节序
#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h表示host,n表示network,l表示long,s表示short。
IP地址4字节(32位),端口号2字节(16位),因此l给IP用,s给Port用。
注意:192.168.1.110是点分十进制IP,本质上是一个String,即“192.168.1.110”字符串。
但是上述函数的参数为无符号整形。
“192.168.1.110”通过atoi()函数转换为32位的int数据。
2. IP地址转换函数
2.1 本地字节序(string IP)----》网络字节序
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
- af: AF_INET, AF_INET6
- src: 传入参数,IP地址(字符串)
- dst:传出参数,转换后的网络字节序的IP地址
“192.168.1.131”---------->网络字节序+的IP
即将本地的小端存储的字符串“192.168.1.131”转换为大端存储的32位二进制数据。
返回值:成功返回1,异常:0,说明src指向的不是一个有效的ip地址
2.2 网络字节序----》本地字节序(string IP)
const char *inet_ntop(int af, const void *src, char *dst, socklen_t len);
af: AF_INET, AF_INET6
src: 网络字节序IP地址
dst:本地字节序(string IP)
size:dst的大小
成功返回dst,失败返回NULL。
即将网络大端端存储的的32位二进制数据转换为本地字节序字符串“192.168.1.131”
3. sockaddr结构体和sockaddr_in的关系
我们在后面的socket编程函数要用到struct sockaddr类型,但是现在大家用的是struct sockaddr_in数据类型,现在通常的做法为,先用sockaddr_in来填充数据,最后再将sockaddr_in强制转换为sockaddr类型。
在网络编程中,我们重点关注sockaddr_in数据结构,最后使用套接字的时候,再将其强制转换为sockaddr结构。
例如:我们使用bind函数的时候,要用到sockaddr结构体
如何做?
1. struct sockaddr_in addr; // 先创建sockaddr_in的结构体
2. addr.sin_family = AF_INET/AF_INET6; // 选用IPv4或IPv6的协议
3. addr.sin_port = htons(9527); // 将主机小端二进制9527转换为网络大端二进制9527
4. addr.sin_addr.s_addr = htonl(INADDR_ANY);//取出系统中有效的 任意IP地址。二进制类型
5. bind(fd, (struct sockaddr *) &addr, size); // 强制转换sockaddr_in为sockaddr类型