socket网络编程

正文

每个socket被创建后,无论使用的是TCP协议还是UDP协议,都会创建自己的接收缓冲区和发送缓冲区。当我们调用write()/send()向网络发送数据时,系统并不会马上向网络传输数据,二十首先将数据拷贝到发送缓冲区,由系统负责择时发送数据。根据我们选用的网络协议以及阻塞模式,系统会有不同的处理。

socket缓冲区的特性:

  • socket缓冲区在每个套接字中单独存在
  • socket缓冲区在创建套接字时自动生成
  • 即使关闭套接字也会继续传送发送缓冲区中遗留的数据
  • 关闭套接字将丢失接收缓冲区中的数据

TCP阻塞模式和非阻塞模式下的数据发送

  1. 阻塞模式下,调用write()/send()后程序将阻塞,如果发送缓冲区的可用长度大于待发送的数据,则数据将全部被拷贝到发送缓冲区,等待系统将发送缓冲区内的数据发送出去,当数据全部被拷贝到发送缓冲区后,阻塞状态将消失;如果发送缓冲区的长度小于待发送的数据长度,则数据能拷贝多少就先拷贝多少(也就是分批拷贝),一直等待直到数据可以全部被拷贝到发送缓冲区为止,调用才可返回。write()/send()调用返回后并不能保证数据已经发送到对方缓冲区了,只能保证数据成功拷贝到发送缓冲区了,至于传输可靠性方面,那就是由系统的协议栈来保证。
  2. 非阻塞模式下,调用write()/send()后,如果发送缓冲区剩余大小大于待发送的数据大小,那数据将完整拷贝到发送缓冲区,如果发送缓冲区剩余大小小于待发送的数据大小,那本次write()/send()则尽可能拷贝,有多少空间就拷贝多少数据,返回值均为成功拷贝到发送缓冲区的数据长度。
  3. 当接收端不接收数据,或者处理速率比发送方的发送速率低,导致其接收缓冲区已满,进而导致数据发送方的发送缓冲区的数据不断堆积进而缓冲区满,此时再调用write()/send()都将阻塞等待。
  4. 系统将发送缓冲区的数据通过网卡发送到网络之后,系统并不会立即将刚发送的数据从缓冲区移除,只有当接收方回复了ack,发送方才能认为对方收到了消息,否则刚发送的数据必须还保留在发送缓冲区等待重传。当系统收到接收方对刚才已发送数据的ack之后,才会移除发送缓冲区内对应的数据,腾出空间。
  5. 因为发送缓冲区满导致write()/send()一直无法返回,这个可以通过setsockopt的参数SO_SNDTIMEO来做超时处理,如果有数据成功拷贝到发送缓冲区,那超时后的返回值是成功拷贝到发送缓冲区的数据长度,如果没有数据拷贝成功,此时的超时后返回值为-1,errno为EAGAIN或EWOULDBLOCK,表现就是跟非阻塞模式的write()/send()是一样的。如果不设置默认就是永不超时,永远阻塞。
  6. socket关闭时,但发送缓冲区中的数据仍未完全成功发送出去,那么这些数据将由系统负责通过可靠的方式发送给对方(其实就是TTCP方式)

TCP阻塞和非阻塞模式下的数据接收

  1. 调用read()/recv()时,如果模式选择的是阻塞模式,那么当接收缓冲区没数据时,程序就会一直拥塞等待,直到有数据可读为止,每次读的数据大小由进程控制,一般都需要分批读取,read()/recv()成功返回时的返回值是成功读取到的数据的长度;如果模式选择的是非阻塞模式,那么程序调用read()/recv()调用返回的返回值是成功读取的字节数,如果没数据可读时,同样是马上返回,此时的返回值是0;返回值如果小于0,表明出现了网络错误。
  2. 如果程序没有及时读取接收缓冲区中的数据,那么缓冲区可利用的空间就会逐渐变小,直到缓冲区满,但TCP套接字的接收缓冲区不可能溢出。这是因为TCP有流量控制策略,根据TCP的流量控制中的滑动窗口机制,接收方会在回复ack时,将窗口大小也回复给发送方,这样如果缓冲区空间为0,发送方也能及时指导停止发送。
  3. 当socket关闭时,如果接收缓冲区还有数据没读取,那么这部分数据将被丢弃。
  4. 跟TCP阻塞写相同,我们可以通过setsockopt的参数SO_RCVTIMEO来对阻塞模式的读做超时处理,如果一段时间内没有数据读取成功,此时的超时后返回值为-1,errno为EAGAIN或EWOULDBLOCK。
  5. recv的第四个参数如果为MSG_WAITALL,则在阻塞模式下不等到指定数目的数据是不会返回的,除非超时时间到了或者连接断开了。

UDP阻塞和非阻塞下的数据发送接收

  1. UDP套接口有发送缓冲区大小(通过SO_SNDBUF修改),不过它仅仅是写道套接口的UDP数据报的大小上限,即UDP没有发送缓冲区。如果一个应用程序写一个大于套接口发送缓冲区大小的数据包,内核将返回EMSGSIZE错误。
  2. 从UDP套接口write()成功返回仅仅表示用户写入的数据报或者所有片段已经加入到数据链路层的输出队列。如果该队列没有足够的空间存放该数据包或者它的某个片段,内核通常返回给应用进程一个ENOBUFS错误。
  3. UDP是没有流量控制的:较快的发送端可以很容易淹没较慢的接收端,当接收缓冲区满后,后面收到的数据报都将丢弃。
  4. UDP存在丢包的可能:调用recvfrom方法接收到数据后,处理数据花费时间太长,再次调用recvfrom,两次调用间隔里,发过来的包可能丢失。处理方法就是调大接收缓冲区或者通过将接收到的数据存入一个缓冲区,并迅速返回继续recvfrom。


标题:socket网络编程
作者:staymeloo7
联系方式:staycoolsun@gmail.com

    评论
    0 评论
avatar

取消