Linux下TCP超时问题

quickSort quickSort 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区
使用TCP的超时选项,目的是在socket读写时防止一直阻塞,在指定的超时时间内仍然不成功则返回错误。
流程:
服务端等待连接,然后开启新线程处理这个连接,处理过程为:先读取一个整数,表示后续需要读的数据长度,然后继续读取这么长的数据,成功后返回一个整数,告诉客户端读到了多少数据。
客户端:主动连接,先发送一个整数,表示后序要发送的数据长度,然后发送这么多数据,然后读取服务器的相应数据(也是一个整数),判断相应是否和预期一致。

出现问题:在2台机器上测试,如果拔掉网线,超时时间总是我设定的2倍,偶尔也会出现3倍;

但是第一次出错后,后面如果继续发送数据,则在指定的超时时间出错返回。
(如果不是拔掉网线,而是直接终止程序,一切正常,总能立刻出错返回,和预期一样)
代码如下:
公共代码:
 //#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>

#include <errno.h>
#include <arpa/inet.h>

#ifndef _SOCKET_H
#define _SOCKET_H

const static int TIMEOUT = 5; /* second */

#define unlikely(x) (x)

static int connect_to(char *ip, unsigned int port)
{
int fd, ret, value = 1;
struct sockaddr_in addr;
struct linger linger_opt = {1, 0};
struct timeval timeout_opt = {TIMEOUT, 0};

addr.sin_family = AF_INET;
addr.sin_port = htons(port);

if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) {
ret = -1;
goto err;
}

fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
ret = -1;
goto err;
}

ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_opt,
sizeof(linger_opt));
if (ret < 0)
goto err_close;

ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));
if (ret < 0)
goto err_close;

ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout_opt,
sizeof(timeout_opt));
if (ret < 0)
goto err_close;

ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout_opt,
sizeof(timeout_opt));
if (ret < 0)
goto err_close;

ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0)
goto err_close;

return fd;

err_close:
close(fd);
err:
fprintf(stderr, "Failed errno:%d(%m)\n", errno);
return ret;
}

static int64_t net_read(int fd, void *buf, int64_t count)
{
char *p = buf;
int64_t sum = 0;
while(count > 0) {
int64_t loaded = 0;
while(1) {
loaded = read(fd, p, count);
if (unlikely(loaded < 0) && (errno == EINTR))
continue;
break;
}

if (unlikely(loaded < 0))
return -1;
if (unlikely(loaded == 0))
return sum;

count -= loaded;
p += loaded;
sum += loaded;
}

return sum;
}

static int64_t net_write(int fd, void *buf, int64_t count)
{
char *p = buf;
int64_t sum = 0;
while (count > 0) {
int64_t written = 0;
while (1) {
written = write(fd, p, count);
if (unlikely(written < 0) && (errno == EINTR))
continue;
break;
}

if (unlikely(written < 0))
return -1;
if (unlikely(written == 0))
return -1;

count -= written;
p += written;
sum += written;
}

return sum;
}

#endif



服务端代码如下:

#include "htime.h"
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>

#include "mysocket.h"

int init_server(int port);
void *recv_work(void *data);
void sig_handler(int signo);


int main(int argc, char *argv[])
{
if(argc != 2)
{
fprintf(stderr, "usage: server port\n");
return 0;
}

int fd = init_server(atoi(argv[1]));
if(fd < 0)
{
fprintf(stderr, "server start failed\n");
return 0;
}

int client;

while(1)
{
fprintf(stderr, "%s waiting for client...\n", gettime());
client = accept(fd, NULL, NULL);
if(client == -1)
{
fprintf(stderr, "%s accept failed : %m\n", gettime());
continue;
}

pthread_t pid;
pthread_create(&pid, NULL, recv_work, &client);

}
}


void *recv_work(void *data)
{
int fd = *(int*)data, ret, len=0;
fprintf(stderr, "client fd: %d\n", fd);
char *buf = NULL;

while(1)
{
ret = net_read(fd, &len, sizeof(int));
fprintf(stderr, "get info from client: %d(%d byte)[%m]\n", len, ret);

if(ret == 0) {
fprintf(stderr, "%s read 0, client seems to be dead...\n", gettime());
break;
} else if(ret < 0) {
fprintf(stderr, "ret=%d, errno=%d:%m\n",ret, errno);
if (errno == EINTR) continue;
if (errno == EAGAIN)
fprintf(stderr, "would block or time out\n");
} else {
buf = malloc(len); if(!buf) {fprintf(stderr, "OOM\n"); break;}
ret = net_read(fd, buf, len);
fprintf(stderr, "get data from client ret:%d ", ret);
if ( ret > 20 ) {
char t[20] = {};
memcpy(t, buf, 20);
fprintf(stderr, "(%s)\n", t);
}

ret = net_write(fd, &ret, sizeof(int));
// free(buf);
// buf = NULL;
}
}
fprintf(stderr, "close client fd: %d\n", fd);
pthread_detach(pthread_self());
pthread_exit(NULL);
}

void sig_handler(int signo)
{
fprintf(stderr, "receivec sig: %d\n", signo);
}

int init_server(int port)
{
struct sockaddr_in ser;
int fd, value = 1;
struct linger linger_opt = {1, 0};

ser.sin_family = AF_INET;
ser.sin_port = htons(port);
ser.sin_addr.s_addr = INADDR_ANY;
bzero(&(ser.sin_zero), 8);

fd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_opt,
sizeof(linger_opt));
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value));

if(bind(fd, (struct sockaddr*)&ser, sizeof(struct sockaddr))== -1)
{
fprintf(stderr, "bind error\n");
return -1;
}
if (listen(fd, 20) == -1){
fprintf(stderr, "listening failed\n");
return -1;
}
signal(SIGPIPE, sig_handler);

return fd;
}




客户端代码如下,:


#include "htime.h"
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>

#include "mysocket.h"

void *recv_work(void *data);
void sig_handler(int signo);


int main(int argc, char *argv[])
{

int p1=8888, p2=8889;
int fd;
if (argc == 2)
fd = connect_to(argv[1], p1);
else
fd = connect_to("10.254.4.23", p1);
if(fd < 0)
{
// fprintf(stderr, "server start failed\n");
return 0;
}
signal(SIGPIPE, sig_handler);
int MAX = 1024*1024*10;
char *buf = malloc(MAX);// 20MB
if (buf == NULL) { fprintf(stderr, "OOM\n"); return 0;}
int ret;

while(1)
{
fprintf(stderr, "press ENTER to continue...");
getchar();
strcpy(buf,gettime());
printf("%s\n", gettime());

ret = net_write(fd, &MAX, sizeof(int));
if (ret != sizeof(int)) {
fprintf(stderr, "%s write failed pos1: ret=%d\n", gettime(), ret);
// continue;
}

ret = net_write(fd, buf, MAX);
if (ret != MAX) {
fprintf(stderr, "%s write failed pos2: ret=%d\n", gettime(), ret);
// continue;
}

int rsp = -1;//
ret = net_read(fd, &rsp, sizeof(int));

fprintf(stderr, "%s ", gettime());
if (rsp != MAX) {
fprintf(stderr, "rsp error: ret=%d,rsp=%d,req=%d \n", ret, rsp, MAX);
} else {
fprintf(stderr, "req & rsp is ok(data length: %d) \n", rsp);
}
}
}

void sig_handler(int signo)
{
fprintf(stderr, "receivec sig: %d\n", signo);
}
...全文
评论 10
收藏 已收藏
分享
返回顶部
quickSort
Linux/Unix社区 23,224 成员 74,535 内容