源目的ip和port相同的链路

    项目需要同一个机器上的测试两个进程间的通信,这两个进程是通过socket来建立链路的,测试过程中发现有的时候即使server端的进程没有运行,客户端也能成功建链,而且这条链路的源目的ip和port都相同,也正是client想要连接的ip和port。开始以为是客户端程序的bug,经过走查代码发现客户端建链的流程是很标准的socket客户端程序,创建套接字调用connect接口。后来将建链的代码抽出来做测试,发现connect多次之后必定可以建一个有问题的链路。网上对这个问题的的说明不多,下面对查到的资料整理总结一下。

    其实,对于没有调用bind的客户端程序,在connect函数中会为相应的套接字选择一个临时接口(源ip和port),注意此时的port是随机的所以很有可能与要连接的服务端的port相同。一旦客户端套接字对应的源ip和port与connect参数中要连接的服务端的ip和port相同,那么这个问题必现。所以,这个问题很容易用如下客户端的程序复现。

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

int main(int argc,char *argv[])
{
    int sockfd,numbytes;
	int times = 0;
    char buf[BUFSIZ];
    struct sockaddr_in their_addr;
    printf("break!");
    while((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1);
    printf("We get the sockfd~\n");
    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(7617);
    their_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    bzero(&(their_addr.sin_zero), 8);
    if (bind(sockfd, (const struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)

   {

       printf("bind failed!");

      return 1;

   }
    while(connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr)) == -1){
		times++;
	};
    printf("Get the Server~Cheers after retry %d times!\n", times);
    numbytes = recv(sockfd, buf, BUFSIZ,0);//接收服务器端信息  
    buf[numbytes]='\0';  
    printf("%s",buf);
    while(1)
    {
        printf("Entersome thing:");
        scanf("%s",buf);
        numbytes = send(sockfd, buf, strlen(buf), 0);
            numbytes=recv(sockfd,buf,BUFSIZ,0);  
        buf[numbytes]='\0'; 
        printf("received:%s\n",buf);  
    }
    close(sockfd);
    return 0;
}

    下面解释下出现问题的原因(摘自https://blog.csdn.net/justlinux2010/article/details/20947609):

       这种类型的连接产生的过程类似于同时打开的情况。同时打开的情况是两个机器同时向另一个机器的已知端口发送SYN段,一个机器上发送的SYN段的目的IP和端口是另一个机器上发送SYN段的套接字的本地IP和端口(注意这两个机器上没有对应端口的监听套接字),状态迁移过程如下图所示:

                                    

      (划重点)这里看到的连接的建立过程只发生在一个机器、一个套接字上,但是过程几乎是一样的。我们假设套接字名称是sk,调用bind将sk套接字的本地IP绑定为192.168.56.101,本地端口绑定为9090。首先,sk向目的IP是192.168.56.101,目的端口是9090的服务器发送SYN段,在发送SYN段之前,协议栈会将sk这个套接字的目的地址设置为192.168.56.101,目的端口设置为9090。当然,这个SYN段肯定是会在本机上进行接收处理。接收到这个SYN段后,会调用__inet_lookup()来查找对应的套接字。由于这个SYN段的源目的IP和端口信息和sk套接字的信息完全匹配,所以会由sk套接字来处理。sk套接字的状态会迁移到SYN_RCVD,然后发送SYN+ACK段。这个SYN+ACK段还是会由本机上的sk套接字处理。在SYN_RCVD状态下接收到SYN+ACK段,套接字的状态会迁移到ESTABLISHED。因为此时sk套接字期望接收的序列号,要比SYN+ACK段的序列号大1,相当于接收到了重复的段,所以还要发送一个D-ACK段,表示接收到了重复的段,但是不会影响sk套接字的状态。状态迁移过程如下所示:

                                                   

参考:https://blog.csdn.net/wangzhjj/article/details/78342943

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金士顿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值