Linux网络编程 TCP通信

TCP三次握手

  1. 客户端发送给服务端

    客户端确认Client的有效。服务端确认Client的有效,Server的有效

  2. 服务端发送给客户端

    客户端确认Client的发、收有效,Server的发、收有效。服务端确认Client的有效,Server的发、收有效

  3. 客户端发送给服务端

    客户端确认Client的发、收有效,Server的发、收有效。服务端确认Client的发、收有效,Server的发、收有效

三次握手之后,客户端和服务器都能确定自己和对方的收、发都没有问题。

为什么要进行这么多次连接,是因为需要确定彼此是否可以接收也可以发送。后面的seq以及ack分别是序号以及确认号,是要确保数据的完整性以及顺序性

对于后面带着的确认号以及序号的规则:

客户端以及服务端的初始值不一样,具体看分配。

当客户端发送第一次消息过去后,假设cseq为1000,那么这个时候服务端的sack的应答,要在这个cseq上进行加1,也就是1001(表示服务端已经接收到1个字节,并且希望下次收到的数据是从第1001个字节开始的)并且也要发送自己的请求连接标志位,这个时候就发送我们服务端的初始值。

当客户端接收到服务端的初始值以及连接信号时,我们会在服务端的初始值上+1,并且付上应答信号返回。

接下来就进入了通信,还是继续客户端的发送消息,这个时候由于我们没有接收到服务端所传送过来的消息,所以我们保留了上一次的cack,并且使用了sack的序号1001,并且规定了发100个字节的内容,真正的内容是1101。

之后服务端为了做好应答工作,返回sack为1101。

之后客户端继续发,那么我们就需要在1101后面继续往后发,所以需要发送1101(200),实则为1301,cack继续保持不变,那么服务端就需要返回1301,这样子就可以确保数据的完整性以及顺序性了。

只有收到SYN和FIN信号,确认信号sack才会+1。其余都是加上具体的数据位。

总结TCP的三次握手:

第一次握手:

  • 客户端将SYN标志置为1。

  • 生成一个随机的32位的序号seq=J,这个序号后面是可以携带数据(数据的大小)。

第二次握手:

  • 服务器端接收客户端的连接:ACK置为1。

  • 服务器会回发一个确认序号:ack=客户端的序号+数据长度(字节)+SYN/FIN(按一个字节算)

  • 服务器端会向客户端发起连接请求:SYN = 1.

  • 服务器会生成一个随机序号:seq = K

第三次握手:

  • 客户端应答服务器的连接请求:ACK = 1。

  • 客户端回复收到了服务器端的数据:ack = 服务端的序号 + 数据长度 + SYN/FIN(按一个字节算)

TCP四次挥手

a想和b断开连接,但吧不一定想,b可能想传完某些数据再断开连接,所以第2、3次挥手都是b发起的。2、3次挥手之间可能割了很长一段时间。可以理解成,b也要考虑一下再作出回答。

注意到最后的标志位TIME_WAIT,这里写着要经过两倍的报文段寿命,有个名词叫2MSL(Maximum Segment Lifetime),主动断开连接的一方,最后进入一次TIME_WAIT状态,这个状态会持续2MSL。

MSL:官方建议是2分钟,实际是30s,主要是保证TCP的安全性以及可靠性,保证对方可以在这个时间段内收到ACK的回应。

当 TCP 连接主动关闭方接收到被动关闭方发送的 FIN 和最终的 ACK 后,连接的主动关闭方必须处于TIME_WAIT 状态并持续 2MSL 时间。

这样就能够让 TCP 连接的主动关闭方在它发送的 ACK 丢失的情况下重新发送最终的 ACK。

主动关闭方重新发送的最终 ACK 并不是因为被动关闭方重传了 ACK(它们并不消耗序列号,被动关闭方也不会重传),而是因为被动关闭方重传了它的 FIN。事实上,被动关闭方总是重传 FIN 直到它收到一个最终的 ACK。

为什么TIME_WAIT等待的时间是2MSL?

  • 确保b能接收到ACK信号

半关闭、端口复用

四次挥手的2、3次挥手之间就是半关闭状态。

当 TCP 链接中 A 向 B 发送 FIN 请求关闭,另一端 B 回应 ACK 之后(A 端进入 FIN_WAIT_2状态),并没有立即发送 FIN 给 A,A 方处于半连接状态(半开关),此时 A 可以接收 B 发送的数据,但是 A 已经不能再向 B 发送数据。

TCP通信并发

多进程实现并发服务器

对于一个终端聊天功能,它只能处理一个TCP会话,所以需要多线程以及多进程来处理并发的问题,主要的思路可以这么处理:

一个父进程,多个子进程。

  • 父进程负责等待并接收客户端的连接。

  • 子进程:完成通信,接受一个客户端连接,就创建一个子进程用于通信。

注意:

发送数据时,一定要:
1、recvBuf[1024] = {0}; 
或者2、write(fd,sendBuf,strlen(sendBuf)+1);
不然sprintf会乱码,最后执行2.把recvBuff中的“\n”也一起发送出去。
​
    char recvBuf[1024] = {0};       //接收
    char sendBuf[1024] = {0};       //发送
    int i = 0;
    int len = 0;
    // 3.通信
    while(1)
    {
        sprintf(sendBuf,"data:%d\n",i++);
        //给服务端发送数据
        write(fd,sendBuf,strlen(sendBuf));
        
        //读取服务器端的数据
        len = read(fd,recvBuf,sizeof(recvBuf));
        if(len == -1)
        {
            perror("read");
            exit(-1);
        }else if(len > 0)
        {
            printf("recv server data: %s\n",recvBuf);
        }else if(len == 0)
        {
            printf("server closed...\n");
            break;
        }
        sleep(1);
    }
  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值