TCP链接异常: SYN_RECV

1. 异常数据包分析:

从数据包分析来看应该是网关这边出问题了,应该是网关的服务程序在1217上出问题了,

(注意左右量变的数据包的一个syn的Seq都是1358143899)

从重发2开始网关这边就一直认为它没有收到client回复给它的ACK,但是从量变的数据包来看,client一直都有收到syn_ack, 并且也回复了每次的ACK到网关,网关的网口也有收到这个ACK,但是服务程序就是认为自己没有收到。图片的左边是tcpclient上的数据包,右边是网关上的数据包。

192.170.146.254是模拟hmi的电脑ip,192.170.146.5是网关的IP。出问题时重新从.254的pc上是可以ssh连接上网关的,重启hmi后,网口也没有使hmi恢复正常(也有重启hmi的动作).

2 补充知识:

tcp的三次握手如下拖所示,也可以看到相关的状态机:

tcp完整状态机:

状态名称

含义

LISTEN

服务端需要打开一个socket进行监听,状态为LISTEN

SYN_SENT

客户端通过应用程序调用connect进行active open.于是客户端tcp发送一个SYN以请求建立一个连接.之后状态置为SYN_SENT(半链接)

SYN_RECV

服务端应发出ACK确认客户端的SYN,同时自己向客户端发送一个ACK. 之后状态置为SYN_RECV(半链接)

ESTABLISHED

代表一个打开的连接,双方可以进行或已经在数据交互了(全连接)

FIN_WAIT1

主动关闭(active close)端应用程序调用close,于是其TCP发出FIN请求主动关闭连接,之后进入FIN_WAIT1状态.等待远程TCP的连接中断请求,或先前的连接中断请求的确认

FIN_WAIT2

主动关闭端接到ACK后,就进入了FIN-WAIT-2

TIME_WAIT

在主动关闭端接收到FIN后,TCP就发送ACK包,并进入TIME-WAIT状态。等待足够的时间以确保远程TCP接收到连接中断请求的确认。(TIME_WAIT状态的形成只发生在主动关闭连接的一方)

CLOSE_WAIT

被动关闭(passive close)端TCP接到FIN后,就发出ACK以回应FIN请求(它的接收也作为文件结束符传递给上层应用程序),并进入CLOSE_WAIT. 等待从本地用户发来的连接中断请求(等待本地应用程序执行close)

LAST_ACK

被动关闭端一段时间后,接收到文件结束符的应用程序将调用CLOSE关闭连接。这导致它的TCP也发送一个 FIN,等待对方的ACK.就进入了LAST-ACK

CLOSING

比较少见,维持时间非常短,等待远程TCP对连接中断的确认

CLOSED

被动关闭端在接受到ACK包后,就进入了closed的状态,本地连接结束。

3. 项目代码(略)

4. 特别解释下面三种状态

SYN_RECV

服务端应发出ACK确认客户端的SYN,同时自己向客户端发送一个SYN. 之后状态置为SYN_RECV,等待客户端回应的ACK,触发accept()完成,如果accept没有完成,状态就会维持。(还有一种情况就是等待队列里的连接数超过listen的第二个参数的个数)

CLOSE_WAIT

被动关闭(passive close)端TCP接到FIN后,就发出ACK以回应FIN请求,但是网关还没有调用close()。比如出问题时,网关的很多连接应该会是这样。

TIME_WAIT

在主动关闭端接收到FIN后,TCP就发送ACK包,并进入TIME-WAIT状态。比如出问题时,HMI的很多连接应该会是这样。

5. 测试代码:

5.1 通过测试代码来控制tcp的流程,然后观察连接状态的变化,使用tcp助手作为client去连接下面的测试代码的8888端口,

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<unistd.h>
#include<arpa/inet.h>
#include <sys/time.h>
#include <unistd.h>

#define MAXLINE 25

int main(int argc, char** argv)
{
    int  listenfd, connfd;
    struct sockaddr_in  servaddr;
    char  buff[25];
    int  n,cnt=0,i;
    int res,ret;


        struct  timeval    tv1,tv2;


    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }



    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr =htonl(INADDR_ANY);
    servaddr.sin_port = htons(8888);
    int on = 1;

    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));


    if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
        printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }

    if( listen(listenfd, 3) == -1){//控制sync队列的大小,方便测试。等待队列里的连接数超过listen的第二个参数的个数
        printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }
        struct sockaddr_in cin;
        socklen_t addrlen = sizeof(cin);

        while(1) sleep(10);//故意不进入accept,让连接状态保持在SYN_RECV

        if((connfd = accept(listenfd,(struct sockaddr *)&cin,&addrlen)) < 0)
        {
            printf("accept err\n");
            exit(1);
        }

        char ipv4_addr[16];
        if(!inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
        {
            printf("inet_ntop\n");
            exit(1);
        }
        printf("Client(%s:%d) is connected!\n",ipv4_addr,htons(cin.sin_port));

    printf("======waiting for client's request======\n");
    while(1){
          cnt++;
                printf("%drecv msg from client:",cnt);
                memset(buff, 0, sizeof(buff));
                n = recv(connfd, buff, MAXLINE, 0);
                if(n > 0)
                    {
                        for(i = 0;i< n;i++)
                       printf("%c ", buff[i]);
                    }

            printf("\n");

            send(connfd, buff, n,MSG_NOSIGNAL);


    }
    close(connfd);
    close(listenfd);
    return 0;
}

5.2 测试代码测试分析如下:

在TCP的三次握手中,走后一个ACK已经从client端(PC侧)发到Service端(网关)的网口上了,但是网关的服务程序没有收到,所以网关的状态维持在SYN_RECV,并且协议栈开始重发SYN_ACK。这个行为和我们在现场抓的数据包的行为是一样的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值