我正在为我的客户端和服务器使用阻塞TCP套接字。每当我阅读时,我首先使用select
检查流上的数据是否可用。我总是一次读写40个字节。虽然大多数读取需要几毫秒或更短,但有些只需要超过半秒。在我知道套接字上有可用数据之后。
我也在使用TCP_NODELAY
可能导致什么原因?
编辑2
我分析了发送和接收的每个数据包的时间戳,并发现只有当客户端在服务器写入下一个对象之前尝试读取对象时才会发生此延迟。例如,服务器写入对象编号x,之后客户端尝试读取对象x,然后服务器才能开始编写对象编号x + 1。这让我怀疑服务器端正在进行某种合并。
修改
服务器正在侦听3个不同的端口。客户端逐个连接到每个端口。
有三个连接:一个经常从服务器向客户端发送一些数据。第二个只从客户端向服务器发送数据。第三个很少用于发送单字节数据。我正面临着第一次连接的问题。我正在使用select()
检查该连接上的数据是否可用,然后当我为40字节读取时间戳时,我发现该读取大约需要半秒钟。
关于如何对此进行分析的任何指示都非常有用
在linux上使用gcc。
rdrr_server_start(void)
{
int rr_sd;
int input_sd;
int ack_sd;
int fp_sd;
startTcpServer(&rr_sd, remote_rr_port);
startTcpServer(&input_sd, remote_input_port);
startTcpServer(&ack_sd, remote_ack_port);
startTcpServer(&fp_sd, remote_fp_port);
connFD_rr = getTcpConnection(rr_sd);
connFD_input = getTcpConnection(input_sd);
connFD_ack= getTcpConnection(ack_sd);
connFD_fp=getTcpConnection(fp_sd);
}
static int getTcpConnection(int sd)
{
socklen_t l en;
struct sockaddr_in clientAddress;
len = sizeof(clientAddress);
int connFD = accept(sd, (struct sockaddr*) &clientAddress, &len);
nodelay(connFD);
fflush(stdout);
return connFD;
}
static void
startTcpServer(int *sd, const int port)
{
*sd= socket(AF_INET, SOCK_STREAM, 0);
ASSERT(*sd>0);
// Set socket option so that port can be reused
int enable = 1;
setsockopt(*sd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
struct sockaddr_in a;
memset(&a,0,sizeof(a));
a.sin_family = AF_INET;
a.sin_port = port;
a.sin_addr.s_addr = INADDR_ANY;
int bindResult = bind(*sd, (struct sockaddr *) &a, sizeof(a));
ASSERT(bindResult ==0);
listen(*sd,2);
}
static void nodelay(int fd) {
int flag=1;
ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0);
}
startTcpClient() {
connFD_rr = socket(AF_INET, SOCK_STREAM, 0);
connFD_input = socket(AF_INET, SOCK_STREAM, 0);
connFD_ack = socket(AF_INET, SOCK_STREAM, 0);
connFD_fp= socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in a;
memset(&a,0,sizeof(a));
a.sin_family = AF_INET;
a.sin_port = remote_rr_port;
a.sin_addr.s_addr = inet_addr(remote_server_ip);
int CONNECT_TO_SERVER= connect(connFD_rr, &a, sizeof(a));
ASSERT(CONNECT_TO_SERVER==0) ;
a.sin_port = remote_input_port;
CONNECT_TO_SERVER= connect(connFD_input, &a, sizeof(a));
ASSERT(CONNECT_TO_SERVER==0) ;
a.sin_port = remote_ack_port;
CONNECT_TO_SERVER= connect(connFD_ack, &a, sizeof(a));
ASSERT(CONNECT_TO_SERVER==0) ;
a.sin_port = remote_fp_port;
CONNECT_TO_SERVER= connect(connFD_fp, &a, sizeof(a));
ASSERT(CONNECT_TO_SERVER==0) ;
nodelay(connFD_rr);
nodelay(connFD_input);
nodelay(connFD_ack);
nodelay(connFD_fp);
}
答案 0 :(得分:1)
我会怀疑这行代码:
ASSERT(setsockopt(fd, SOL_TCP, TCP_NODELAY, &flag, sizeof flag)==0);
如果您正在运行发布版本,那么ASSERT很可能被定义为什么,因此实际上不会进行调用。 setsockopt调用不应该在ASSERT语句中。相反,应该在assert语句中验证返回值(在变量中)。 Asserts with side effects通常是一件坏事。所以即使这不是问题,也应该改变它。
答案 1 :(得分:0)
一个客户端和多个连接?
某些套接字函数可能会阻止您的执行(即等待函数的结果)。我建议为每个连接打开一个新线程(在服务器端),这样它们就不会相互干扰......
但是我在黑暗中射击;你需要发送一些额外的信息......答案 2 :(得分:0)
您的陈述仍然令人困惑,即“只有一个客户端的多个tcp连接”。显然,您有一台服务器在一个端口上侦听。现在,如果您有多个连接,这意味着有多个客户端连接到服务器,每个客户端连接在不同的tcp客户端端口上。现在服务器运行select并响应任何客户端有数据(意味着客户端在他的套接字上发送了一些数据)。现在,如果两个客户端同时发送数据,服务器只能按顺序处理它们。因此,在服务器完成第一个处理之前,第二个客户端将不会得到处理。
选择仅允许服务器监视多个描述符(套接字)和具有可用数据的进程。它并不像是并行处理。你需要多个线程或进程。
答案 3 :(得分:0)
也许它与timeout
参数有关。
您为timeout
来电的select
参数设置了什么?
尝试将超时参数更改为更大的参数并观察延迟。有时太小的超时和很多时候系统调用实际上可以杀死吞吐量。如果您假设延迟稍大一点,也许可以实现更好的结果,这是可以实现的。
我怀疑超时或一些代码错误。
答案 4 :(得分:0)
您可以尝试使用TCP_CORK
(CORK'ed模式),内核扩展GRO
,GSO
和TSO
被ethtool
禁用:
在TCP_CORK
标记的会话中发送将确保数据不会在部分段中发送
禁用generic-segmentation-offload
,generic-receive-offload
和tcp-segmentation-offload
将确保内核在将数据移入/移出用户空间之前不会引入人工延迟来收集其他tcp段