两个应用程序同时彼此执行主动打开的情况是可能的,但是发生的可能性极小。每一方必须发送一个SYN,且这些SYN必须传递给对方。这需要每一方使用一个对方熟知的端口作为本地端口。这又称为同时打开。两端必须几乎在同时启动,以便收到彼此的SYN。只要两端有较长的往返时间就能保证这一点。
TCP是特意设计为了可以处理同时打开,对于同时打开它仅建立一条连接而不是两条连接(其他的协议族,最突出的是OSI运输层,在这种情况下将建立两条连接而不是一条连接)出现同时打开的情况时,两端几乎在同时发送SYN,并进入 SYN_SENT状态。当每一端收到SYN时,状态变为SYN_RCVD,同时它们都再发SYN并对收到的SYN进行确认。当双方都收到SYN及相应的ACK时,状态都变迁为ESTABLISHED。
在同时打开的情况下,即使客户端没有开启监听依然会建立连接。
看完同时打开应该明白了为什么会出现子连接,当客户端调用connect连接自身ip时,此时内核随机分配一个端口给客户端(这里并不是真正的随机而是通过计数器来实现的)(其范围可通过sys -A|grep range来查看),若此时分配的端口号等于目的端口号,则会出现自连接。
客户端发送syn后等待服务器返回ack,而此时客户端发的syn报文从网卡绕了一圈又回到了内核被自己收到,而此时客户端只会认为这个报文是另一台机器发过来的,触发‘同时打开’,发送ack报文,ack报文发送回来后 ,客户端认为是自己发送的第一条请求发回来了,就成功建立起连接,而以后的传输报文都被客户端看成是与服务器同时发的情况,所以ack报文还是能正常收发。
1、监听端口设置在客户端ip_local_port范围外
2、建立连接后判断是否是自连接(getsockname、getpeername)
#include#include #include #include #include #include #include #include int main(int args,char*argv[]) { if(args<3){printf("using ip and portn");return -1;} struct hostent* h; struct sockaddr_in clientaddr,servaddr; if((h = gethostbyname(argv[1]))==NULL) { printf("gethostbyname failed:%sn",strerror(errno)); return -1; } servaddr.sin_family = AF_INET; servaddr.sin_port = htons(atoi(argv[2])); memcpy(&servaddr.sin_addr,h->h_addr,h->h_length); int socklen = sizeof(struct sockaddr_in); int sockfd; int n = 65536; int min,max; min = max = 50000; while(n--) { memset(&clientaddr,0x00,sizeof(clientaddr)); sockfd = socket(AF_INET,SOCK_STREAM,6); if(sockfd<0){printf("socket failedn");continue;} if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(clientaddr))!=0) { if(getsockname(sockfd,(struct sockaddr*)&clientaddr,(socklen_t*)&socklen)!=0) {printf("%sn",strerror(errno)); close(sockfd);return -1;} min = min htons(clientaddr.sin_port)?max:htons(clientaddr.sin_port); printf("connect failed(%d)n",ntohs(clientaddr.sin_port)); close(sockfd); continue; } if(getsockname(sockfd,(struct sockaddr*)&clientaddr,(socklen_t*)&socklen)!=0) {printf("%sn",strerror(errno)); close(sockfd);return -1;} printf("connect success(%d)n",ntohs(clientaddr.sin_port)); break; } printf("min=%d,max=%dn",min,max); if(n<0)return -1; char buffer[10]; for(int i =0;i<10;++i) {buffer[i] = "0123456789ABCDEF"[i%16];} int iret; for(int i = 0;i<2;++i) { iret = send(sockfd,buffer,sizeof(buffer),0); if(iret<0){printf("send failed:%sn",strerror(errno));close(sockfd);return -1;} if(iret==0){printf("close connectn");close(sockfd);} printf("发送:%sn",buffer); iret = recv(sockfd,buffer,sizeof(buffer),0); if(iret<0){printf("recv failed:%sn",strerror(errno));close(sockfd);return -1;} if(iret==0){printf("close connectn");close(sockfd);} printf("接收:%sn",buffer); } }



