当tcp服务端全连接队列满了后,并且服务端也不accept取出连接,客户端再次连接时,服务端能够看到SYN_RECV状态。但是客户端看到的是ESTABLISHED状态,所以客户端自认为成功建立了连接,故其写往服务端写数据,发现数据也确实写成功了。
但是后面等到服务端通过ACK+SYN告知客户端重新ACK时,发现此时客户端的业务数据已经到来了,故而认为出了问题,故重置连接。
对应的代码如下:
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>#define PORT 8888 //端口地址:8888
#define SERVER_IP "10.0.0.64"extern void process_conn_client(int s);int main(int argc,char *argv[])
{int s = 0; //socket描述符struct sockaddr_in server_addr; //服务器地址结构int ret = 0; //返回值char buf[1024] = {0};int i = 0;strcpy(buf, "hello world");/*** Step 2 : 建立套接字*/s = socket(AF_INET,SOCK_STREAM,0); //创建一个AF_INET族的流类型socketif(s < 0) //检查是否正常创建socket{perror("socket error\n");exit(EXIT_FAILURE);}/*** Step 3 : 设置服务器地址*/memset(&server_addr, 0, sizeof(server_addr)); //清零server_addr.sin_family = AF_INET; //设置地址族为AF_INETinet_pton(AF_INET, SERVER_IP, &(server_addr.sin_addr));server_addr.sin_port = htons(PORT); //设置端口号/*** Step 4 : 将用户输入的字符串类型的IP地址转为整型*///inet_pton(AF_INET,argv[1],&server_addr.sin_addr);/*** Step 5 : 连接服务器*/ret = connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));if(ret < 0){perror("connect error\n");printf("errno is %d\n", errno);exit(EXIT_FAILURE);}printf("connect succeed\n");//for(;;){//sleep(10000000);sprintf(buf, "hello world, %d", i);i++;ret = write(s,buf,strlen(buf)+1);if(ret < 0){printf("write failed, errno is %d\n", errno);}ret = read(s, buf, strlen(buf)+1);if(ret < 0){printf("read failed, errno is %d\n", errno);}}while(1)sleep(10);close(s); //关闭连接
}
服务端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>#define PORT 8888 //侦听端口地址:8888
#define BACKLOG 20 //侦听队列长度:2extern void process_conn_server(int s); //服务器对客户端的处理:读取数据并发送响应字符int main(int argc,char *argv[])
{int ss = 0; //ss = server socket = 服务器socket描述符int cs = 0; //cs = client socket = 客户端socket描述符struct sockaddr_in server_addr; //服务器地址结构struct sockaddr_in client_addr; //客户端地址结构int ret = 0; //返回值pid_t pid;//进程IDchar buffer[1024];ssize_t size = 0;/*** Step 2 : 建立套接字*/ss = socket(AF_INET,SOCK_STREAM,0); //创建一个AF_INET族的流类型socketif(ss < 0) //检查是否正常创建socket{perror("socket error\n");exit(EXIT_FAILURE);}/*** Step 3 : 设置服务器地址* Note:* htonl():将主机数转换成无符号长整型的网络字节顺序* htons():将整型变量从主机字节顺序转变成网络字节顺序*/bzero(&server_addr,sizeof(server_addr)); //清零server_addr.sin_family = AF_INET; //设置地址族为AF_INETserver_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址server_addr.sin_port = htons(PORT); //设置端口号/*** Step 4 : 绑定地址结构到套接字描述符*/ret = bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr));if (ret < 0) //出错{perror("bind error\n");exit(EXIT_FAILURE);}/*** Step 5 : 设置侦听,侦听队列长度为2,可同时处理两个客户端连接请求*/ret = listen(ss,BACKLOG);if (ret < 0) //出错{perror("bind error\n");exit(EXIT_FAILURE);}/*** Step 6 : 主循环过程*/sleep(6000);for(;;){/* 接收客户端连接 */int addrlen = sizeof(struct sockaddr);cs = accept(ss,(struct sockaddr*)&client_addr,&addrlen);if(cs < 0) //出错{continue; //结束本次循环}//sleep(30);/*for(;;){size = read(cs,buffer,1024); //从套接字中读取数据放到缓冲区buffer中if(size == 0) //没有数据{return;}printf("buffer is %s\n", buffer);}
*/}return 0;
}
客户端通过下面脚本并发投递100个:
#!/bin/bashfor i in {1..100}
do
{./tcpclient &
}
done
最终可以看到客户端的打印情况:
其中104代表连接被对端重置。