Linux Socket 编程 --- Posix API 详解

Linux 操作系统为 网络编程提供了很多系统级别的接口,这篇文章主要讲述 这些接口以及相应的原来,希望能写清楚。

系统接口

服务端

 socket()  本质是系统分配一个fd 并配上  tcb
 bind()   
 listen()   把tcb 置为 listen 的状态
 accept()   为全连接队列分配一个fd
 recv()
 send()  
 close()  fin 的包 放到 包里面,并回收fd

客户端

socket()
bind(optional)
connect()   向对方发送sync 包,connect 是阻塞的么?有等待,连接被拒绝。
send()
recv()
close()

那么这些API函数对应了 TCP 协议的 三次握手,数据传输, 四次挥手 的哪些过程呢?

三次握手

Client Server Client 发送第一次握手 Sync包 Client 使用 Connect() 接口完成发送 Server 此时已经 socket bind listen 状态 syn ,seqnumber=1234 1 Server 回复并发送Sync 包 Server 端利用 accept() 完成三次握手 Server 端有 TCB 半连接队列,存储一次握手 syn seqnumber=4567, ack acknum = 1235 2 Client 回复Server 的Sync ack acknumber=4568 3 Server 端有 TCB 全连接队列 Client Server

在这里插入图片描述

半连接队列和全连接队列
backlog 有其历史原因,可以是 半连接队列的长度,也可能是全连接队列的长度。需要在系统钟确定
man listen 查看 backlog 的解释

accept() 怎么在三次握手之后知道 有连接?
阻塞
全连接队列为空的时候,会一直在等待
非阻塞
全连接队列为空的时候,返回-1

什么是SYN 呢?
SYN 如图所示是TCP 包头的一位数据。
TCP 报文
序号(seq)
TCP是面向字节流的。在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。整个要传送的字节流的起始序号必须在连接建立时设置。首部中的序号字段值则是指的是本报文段所发送的数据的第一个字节的序号。长度为4字节,序号是32bit的无符号数,序号到达2^32 - 1后又从0开始。
确认号(ack)
ack:确认序号,更确切地说,是接收端所期望收到的下一个序号。

确认序号为上次接收的最后一个字节序号加1.只有确认标志位(ACK)为1的时候,确认序号才有效。

三次握手 之后 Client 端与 Server 端状态变化
note: SYN_SENT 与 SYN_RCVD 状态可以转换。
在这里插入图片描述
Server 端由 Close 状态 ,通过 系统函数 (socket()) 创建 socket , bind() 绑定端口,listen() 监听,进入 LISTEN 状态,等待连接。
Client 端 也 创建socket, bind() 可选, Connect() 发送SYN ,第一次握手。进入到 SYN_SENT状态。
Server 端 收到 Client 端 发送的SYN,返回 ACK 并再次发送SYNC 后,进入到 SYN_RCVD 状态
Client 端 收到 Server 端 发来的确认信息和 SYNC 信息后 回复 后进入 ESTABLISHED 状态
Server 端 收到 Client端的回复 也进入ESTABLISHED 状态

这样 TCP 建立链接就完成了。。。下面就是收发信息了。

Note: 监听状态的socket 也可以连接接其他服务器的,这个做个实验观察一下

数据传输

在这里插入图片描述
send()的工作原理

send()函数只负责将数据提交给协议层。当调用该函数时,send()先比较待发送数据的长度和套接字的发送缓冲区的长度:

·当待拷贝数据的长度大于发送缓冲区的长度时,该函数返回SOCKET_ERROR;

·当待拷贝数据的长度小于或等于发送缓冲区的长度时,那么send先检查协议是否正在发送发送套接字的发送缓冲区中的数据:

如果是就等待协议把数据发送完,再进行拷贝;

如果协议还没有开始发送套接字的发送缓冲区中的数据或者该发送缓冲区中没有数据,那么send就比较该发送缓冲区中的剩余空间和待拷贝数据的长度:

如果待拷贝数据的长度大于剩余空间的大小,send就一直等待协议把该发送缓冲区中的数据发完;

如果待拷贝数据的长度小于剩余空间大小,send就仅仅把buf中的数据拷贝到剩余空间中。(注意:并不是send把该套接字的发送缓冲区中数据传到连接的另一端,而是协议传的,send仅仅是把数据拷贝到该发送缓冲区的剩余空间里面。)

如果send函数拷贝成功,就返回实际拷贝的字节数;如果拷贝的过程中出现错误,send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。

要注意,send函数把buffer中的数据成功拷贝到套接字的发送缓冲区中的剩余空间里面后,它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传输过程中出现网络错误的话,那么下一个socket函数就会返回SOCKET_ERROR。(每一个除send外的socket函
数在执行的最开始总要先等待套接字的发送缓冲区的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该socket函数就返回SOCKET_ERROR。)

** recv()的工作原理**
recv先检查套接字的接收缓冲区,如果该接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把套接字的接收缓冲区中的数据拷贝到用户层的buffer中,(注意:协议接收到的数据可能大于buffer的长度,所以在这种情况下,要调用几次recv函数才能把套接字接收缓冲区中的数据拷贝完。)recv函数仅仅是拷贝数据,真正的接收数据是协议来完成的。

recv函数返回其实际拷贝的字节数。如果recv在拷贝时出错,那么就返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。对方优雅的关闭socket并不影响本地recv的正常接收数据,如果协议缓冲区内没有数据,recv返回0,指示对方关闭;如果协议缓冲区有数据,则返回对应数据(可能需要多次recv),在最后一次recv时,返回0,指示对方关闭。

为什么会出现发送不成功

公网情况复杂会出现丢包的现象。
send 是把 消息放到内核态的buffer 里面就不用去管了。
五元组( srcip,dstip,srcport,desport,protocol)
TCB (tcp control block)

send 发送环节

send 发送成功不代表成功。

  1. 单个 send
  2. 多个send
  3. 循环 send

TCP 的分包 与 粘包解决

  1. 在应用层协议头前加上 pktlen 包长度
    read(tcphdr,2); // 读取包长度
    read(tcphdr->length); // 读取剩余的长度
    while(count < tcphdr->length){
    size = read(tcphdr->length -count);
    count + = size;
    }
  2. 为每一个包加上分割符
    读取固定长度
    read(buffer,1024);
    buffer[idx] =“\r\n”;
    保留 pkt = &buffer[idx+2]
    需要用到 ringbuffer 的东西。

四次挥手

在这里插入图片描述
客户端宕机只能由 应用层的协议去检测。

close() 函数本质也是一个 send(),对方收到 recv 返回为0.

此时 server 端程序再调用close() 给客户端 发送 Fin
在这里插入图片描述
注意同时关闭

出现大量close_wait 是什么原因,怎么解决

Server 收到了 大量 client 主动 发送过来的 关闭请求。
解决方法: recv = 0 时使用
recv=0 与close 做成异步

双方同时调用 close()

在这里插入图片描述
Closing 状态 与 TIME_WAIT
TIME_WAIT 系统默认可改

为什么会有time_wait

防止 last_ack 丢了。丢了之后 TCP 会重发。

文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:
服务器高级架构体系:
https://ke.qq.com/course/417774?flowToken=1010783

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值