Linux网络编程之socket编程(四)-readn,writen

至此,我们已经学习了简单的回射/客户服务器,处理多客户连接以及点对点的通信等内容。今天我们将学习一下内容:流协议与粘包、粘包产生的原因、粘包处理方案、readn和writen以及对回射客户/服务器的一些改进。

1.流协议与粘包

我们知道TCP是基于字节流的传输服务,意味着TCP传输的数据之间是没有边间的;而UDP是基于消息的传输服务,传输的是数据报,是有边界的。

这就导致了TCP传输过程会产生一个问题——粘包问题

2.粘包产生的原因

(1)应用程序通过套接字调用一个write方法,将应用层缓冲区中的数据拷贝到套接口发送缓冲区中,而发送缓冲区有SO_SNDBUF大小的限制。若应用层一条消息的缓冲区大小超过发送缓冲区大小,就可能会被分隔。

(2)TCP传输有最大段MSS(maximum segment size)大小的限制,也有可能产生粘包问题。

(3)链路层传输数据有最大传输单元MTU的限制,如果传输数据超过MTU就会在IP层进行分片。也会导致粘包问题。

(4)其他情况,例如TCP拥塞控制等也会导致粘包问题。

如下图所示:TCP粘包原因

3.如何解决粘包问题

本质上是要在应用层维护消息与消息的边界,有如下解决方案:

  • 定长包
  • 包尾加\r\n(ftp)
  • 包头加上包体长度
  • 更复杂的应用层协议
4.readn、writen函数的封装

用来接收和读取确切数目的读写操作。

(1)发送定长包

如果单单是改进发送定长包也存在一种“浪费”情况:比如服务端和客户端规定了缓冲区大小为1024字节,即每次发送、接收数据都要处理1024字节,尽管发送的数据未到1024字节,这就增加了网络负担。那么,怎么处理这种情况呢?

(2)我们自己定义协议,定义一个包的结构体来应对上述情况。

1)发送时,一共发送字节数为头部字节4(int 类型为4字节)加上实际字节n(sendbuf.buf)。

2)接收时,分两次接收:先接收4个字节(recvbuf.len),然后根据recvbuf.len(假设recvbuf.len为n)的长度,接受n个字节。

(3)完整源码如下:

echosrv.c

echoscli.c

总结:
(1)在处理粘包问题时,加上了自定义的协议——包头+包体,从而实现了消息与消息的边界。
(2)包头就是数据长度,包体是数据部分
(3)对方接收的时候,先接收长度(固定是4个字节),然后根据这4个字节计算包体的实际数据长度再接收n个字节。

发表评论

电子邮件地址不会被公开。 必填项已用*标注