网络编程实战(一:基础篇)

一,Unix/Linux系统,TCP/IP协议

历史:此处省略5万字

二,网络编程模型的基本概念

1,客户端-服务端的网络编程模型:三段论,客户端按照双方约定的格式,向服务端发送请求;服务端按约定的格式解释请求,并开始处理;服务端将处理结果按照同样的格式编码,并响应给客户端。无论是客户端还是服务端,它的运行单位都是进程,而不是机器,因此,对于一个终端,同一时刻可以建立多个不同的服务器连接。同一个服务器,也可以运行多个服务。

2,IP和端口:IP地址全网唯一,标识某一台机器,端口是一个16位的整数,最大为65535。服务端监听在一个众所周知的端口上,客户端端口是发起连接请求时,系统内核临时分配的。四元组唯一确定一个连接,也叫套接字对。(客户端IP:端口,服务端IP:端口)

3,保留网段:在IPV4的地址空间里,专门划出3个保留网段,仅仅作为机构内部使用。

4,子网掩码

1),网络:一组IP中的共同部分。比如,192.168.1.1-192.168.1.255的区间里,192.168.1.0代表所在的网络。

2),主机:一组IP中的不同部分,比如,上例中的1-255,表示在这个网络中,有255个可用的IP地址。

3),子网掩码:决定一个IP所在子网的网络地址,它能用任意位数表示,每一位都是1。通过与网络掩码的“位与操作”,可以切出一个IP所在的子网,以及它对应的主机号。子网掩码有不同的表示方法。

5,全球域名系统(DNS):记录并查找网站地址和其对应IP地址的映射关系。

6,字节流和数据报:TCP/UDP协议。

三,套接字和地址

1,服务端:首先初始化监听socket,将其绑定bind在一个众所周知的地址和端口上,接着开启监听listen,最后阻塞在accept上等待客户端连接请求到来。

2,客户端:首先初始化连接socket,接着向服务端的地址和端口发起连接connect请求,执行TCP三次握手。

3,数据传输:连接建立后,客户端向内核发起write的系统调用执行写操作,发送请求,数据从应有程序被拷贝到内核协议栈,协议栈将字节流通过网络设备传输到服务端的内核协议栈,服务端通过read系统调用,将协议栈中客户端传输的数据,拷贝到应用程序中,进行解析,并执行业务处理后,以同样的方式写给客户端进行响应。因此,一旦连接建立,数据传输是双向的,这点需要被牢记!

4,连接关闭:当客户端需要和服务端断开连接时,调用close函数。此时发生的操作是,系统内核向该连接链路上的服务端发送一个FIN包,服务端收到后执行被动关闭,此时,客户端在收到服务端反馈前,认为连接是正常的,此时整个链路处于半关闭的状态。当服务端执行被动关闭时,也会调用close函数,此时整个链路才进入全关闭状态,双方都会感知到连接已关闭。

5,套接字地址格式:通用地址结构(16字节),IPV4地址结构(16字节),IPV6地址结构(28字节),本地地址结构(最多110字节:本地文件无需端口,路径不同地址可变)。

使用通用地址结构,是对其他地址结构的抽象,有一个可以统一操作的地址结构就可以设计统一的网络编程接口。开发人员在具体实现中,再根据通用地址内的协议族,强制转换为指定的地址类型即可。

6,HTTP,WebSocket的区别和联系:HTTP是应用层协议,基于TCP Socket实现,通常是短连接,客户端只能不断轮询从服务端获得消息。WebSocket是对HTTP的增强,利用TCP双向特性,增强服务端到客户端的传输能力,服务端可以直接推送消息到客户端。

四,连接与收发

1,服务端监听过程:

1),socket创建套接字:domain:协议类型(IPV4,IPV6,LOCAL);type(字节流,数据报)

2),bind绑定地址端口:fd:监听套接字;addr:服务端通用地址指针(前两个字节,判断协议类型);len:addr的地址长度(处理可变长度);

绑定时,地址和端口有多种处理方式:

如果绑定指定地址,则系统内核只会接收目标地址是该指定地址的IP包,供服务端处理。但如果一个服务器上有多个网卡,这个方式显然不合理,因为有多个地址都对应这台服务器,但服务端只接收一个地址上数据包。所以,可以考虑在服务端使用通配地址,即IPV4:INADDR_ANY;IPV6:IN6ADDR_ANY。

如果绑定端口0,则服务端会把端口选择交给系统内核,内核会根据算法选择特定端口,这显然也不合理。因为,服务端的端口通常需要绑定在众所周知的非保留端口上。

3),listen开启监听:socketfd:监听套接字;backlog:未完成连接队列的大小,表示socketfd可读,可以accpet建立连接,但还没来得及accept的连接数。Linux不允许改变对该参数。

默认情况下,创建一个普通的socket,被认为是需要主动发起请求的客户端套接字。通过listen函数,可以将“主动”转换为“被动”,系统内核会做好被动接收请求的准备,初始化对应的数据结构,比如,完成连接队列。

4),accept接收连接:listensockfd:监听套接字;cliaddr:客户端地址;addrlen:客户端地址长度。返回值:连接套接字,服务端可以通过该返回的套接字,与特定客户端通信。

2,客户端连接过程:

1),socket创建套接字:同上

2),connect发起连接:sockfd:连接套接字;servaddr:服务端地址;addrlen:地址长度。

3),开始三次握手:成功或出错后,才返回。出错的情况:1)客户端SYN包无响应,客户端返回TIMEOUT超时错误,通常是服务端IP填错;2)客户端收到RST(复位)响应,客户端返回CONNECTION REFUSED错误,通常是端口写错,因为RST表示目的端口的SYN包到达,但该端口并未开启任何监听,TCP发现它接收到一个根本不存在的连接上的数据包,取消该连接;3)客户端SYN包目的地不可达,通常是服务端与客户端的路由不通。

3,客户端发送数据:

1),常使用的函数:write,send和sendmsg,场景略有不同,都是以socketfd为目标写,但send和sendmsg分别还可用户发送带外紧急数据和多重缓冲区传输。和写普通文件不同的时,write在普通文件写中,真正写入的大小通常与传入的size相同,而对套接字而言,真正写入的大小有可能比传入的size小,原因和系统内核建立的发送缓冲区有关。

2),发送缓冲区:SO_SNDBUF,write函数实际上是将数据从应用程序拷贝到系统内核的发送缓冲中,由内核决定何时发送。因此,对于阻塞套接字来说,如果剩余发送缓冲区够大,写入的数据量就是请求的数据量,相反,如果剩余发送缓冲区足够小,wirte会阻塞挂起,直到内核发送完数据,腾出足够的空间后,write写入全部数据后返回。而对非阻塞套接字,能发送多少就返回多少,返回值是写入发送缓冲区的长度。

3),无限增大系统内核缓冲区,可以减少write和read系统调用,降低状态的切换次数,但对吞吐量意义不大,内核发送时,会受到IP包MTU的最大限制。

4,服务端接受数据:

1),常使用的函数:read,以socketfd为目标读,要求最多读取的字节数,返回实际读取的字节数。阻塞套接字下的特殊返回值:返回0,表示对端发送close连接,接受到它发送的FIN包,已无数据可读,要处理断连的情况;返回-1,表示读取出错。对非阻塞套接字来说,返回-1时,还需要判断errno的值,后面细讲。

2),接收缓冲区:同样,read是指程序将数据从系统内核拷贝至应用程序。阻塞套接字会挂起,直到有数据返回。而非阻塞套接字会直接返回。

五,别忘了UDP

1,最大区别:如果说TCP是日常普通打电话,面向连接,在IP报文的基础上,增加了重传,确认,有序和拥塞控制等能力,有一个确定的上下文;UDP就是邮筒投递明信片,不可靠通信,有效,有序,拥塞啥都没有。

2,使用场景:UDP比较简单,常见的DNS服务,SNMP服务,聊天室,游戏等都使用UDP,对丢包不敏感,丢一些不会影响实际体验。

3,UDP编程:通过recvfrom和sendto直接收发数据,若服务端不存在,客户端会阻塞在recvfrom,而不是报错返回,应用层需要增加超时处理。若服务端开启->关闭->开启,不影响收发数据的正常运行,无需重新建立连接,表示UDP无上下文。

4,最大报文长度:TCP包头中无“包长度字段”,完全依靠IP层MTU去处理分帧,协议本身也会进行拥塞控制和流量控制,所以它被称为“流协议”。UDP包头中的包长度占2个字节,最大发送65535字节的数据,扣除UDP头的8个字节和IP头的20个字节,UDP包最多发送65507字节的数据。IP包的最大长度受到数据链路层1500个字节(不含链路层包头)的限制,因此,对UDP包来说,它在IP层的MTU是1500-20(IP头)-8(UDP头)=1472字节;TCP的MTU是是1500-20(IP头)-20(TCP头)=1460字节。

六,还有本地套接字

本地套接字是IPC,是一种本地进程间的通信方式,除此之外,还有管道,共享内存,共享消息队列等。

1,最大区别:使用TCP/IP的127.0.0.1完成在一台机器上客户端和服务端的进程通信,也要通过网卡回环走网络协议栈,而本地套接字本身就是一种单主机跨进程的调用手段,不会通过网络,效率更高;因此,本地套接字地址只需要输入协议号AF_LOCAL和以绝对路径表示的socket文件名即可,与端口无关。

2,错误场景:当只启动客户端时,文件套接字上没有被监听,客户端直接报错,提示文件不存在。当服务端监听在没有权限的文件上,服务端直接报错,提示无权限监听。

3,使用方式:本地套接字和其他套接字的编程接口相同,同时支持数据流和数据报2种协议;因为不走网络协议栈,因此效率上大大高于前两者,可以作为同一机器不同进程间的IPC通信工具使用。

七,学习网络编程的6大工具

1,ping,网络连通性测试,基于ICMP协议,是一种基于IP的控制协议,与TCP或UDP无关;源主机组装ICMP协议发送报文,IP报文通过ARP协议,源地址和目的地址都被翻译成MAC地址,经过数据链路层后发送出去,目的主机再以相同的方式应答。

2,tracerout,网络路由转发测试,也是基于ICMP协议,使用TTL计数的方式,检查报文从源地址到目标地址所经过的路由器。

3,ifconfig,显示当前系统的网卡列表。详细显示了网卡支持的协议,IP地址,MAC地址,广播地址,子网掩码,Metric等等信息。广播地址是子网中一个特殊的被保留的地址,向该地址发消息,子网络中的所有主机都能收到。通常是使用UDP实现的。当本机向目的地址发起连接时,数据可以从多个网卡发送出去,具体的网卡优先级选择由Metric来决定,数值越小,优先级越高。

4,netstat:帮助了解本机上所有的网络连接状态,并明晰连接详情。在本机端口不足时,用来检查无效的TIME_WAIT连接最有效。

5,lsof:帮助找出指定IP地址和端口上打开的套接字进程。在本机地址被占用时,排查正在使用该端口的进程最有用。

6,tcpdump:抓包。开启抓包时,tcpdump会自动创建一个AF_PACKET协议的网络套接口并向内核注册。当网卡接收到一个网络报文后,会遍历所有已注册的网络协议,并调用绑定在其上的回调函数,回调函数内可以自行处理,比如,完整复制一份报文等,再转交给tcpdump程序进行条件的选择和过滤。

八,问题:一段数据流从服务端进程至远端的客户端进程,数据包会经过几次拷贝?

发布了37 篇原创文章 · 获赞 19 · 访问量 883

猜你喜欢

转载自blog.csdn.net/zuiyijiangnan/article/details/103013437