我试图测量我正在编写的TCP服务器的速度,我注意到可能存在测量connect()调用速度的根本问题:如果我以非阻塞方式连接,请连接()操作在几秒钟后变得非常慢.以下是
Python中的示例代码:
#! /usr/bin/python2.4 import errno import os import select import socket import sys import time def NonBlockingConnect(sock,addr): #time.sleep(0.0001) # Fixes the problem. while True: try: return sock.connect(addr) except socket.error,e: if e.args[0] not in (errno.EINPROGRESS,errno.EALREADY): raise os.write(2,'^') if not select.select((),(sock,),(),0.5)[1]: os.write(2,'P') def InfiniteClient(addr): while True: sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) sock.setblocking(0) sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # sock.connect(addr) NonBlockingConnect(sock,addr) sock.close() os.write(2,'.') def InfiniteServer(server_socket): while True: sock,addr = server_socket.accept() sock.close() server_socket = socket.socket(socket.AF_INET,0) server_socket.setsockopt(socket.SOL_SOCKET,1) server_socket.bind(('127.0.0.1',45454)) server_socket.listen(128) if os.fork(): # Parent. InfiniteServer(server_socket) else: addr = server_socket.getsockname() server_socket.close() InfiniteClient(addr)
使用NonBlockingConnect,大多数connect()操作都很快,但是每隔几秒就会发生一次connect()操作,至少需要2秒(由输出上的5个连续P字母表示).通过使用sock.connect而不是NonBlockingConnect,所有连接操作似乎都很快.
怎么可能摆脱这些慢连接()?
我正在使用标准PAE内核运行Ubuntu Karmic桌面:
Linux narancs 2.6.31-20-generic-pae #57-Ubuntu SMP Mon Feb 8 10:23:59 UTC 2010 i686 GNU/Linux
奇怪的是strace -f ./conn.py没有延迟.
奇怪的是,如果我取消注释非常快的时间,就没有延迟.sleep.
奇怪的是,我的Ubuntu Hardy系统没有延迟:
所有这些系统都受到影响(运行Ubuntu Karmic,Ubuntu Hardy,Debian Etch):
Linux narancs 2.6.31-20-generic-pae #57-Ubuntu SMP Mon Feb 8 10:23:59 UTC 2010 i686 GNU/Linux Linux t 2.6.24-grsec #1 SMP Thu Apr 24 14:15:58 CEST 2008 x86_64 GNU/Linux Linux geekpad 2.6.24-24-generic #1 SMP Fri Sep 18 16:49:39 UTC 2009 i686 GNU/Linux
奇怪的是,以下Debian Lenny系统不受影响:
Linux t 2.6.31.5 #2 SMP Thu Nov 5 15:33:05 CET 2009 i686 GNU/Linux
仅供参考我使用AF_UNIX插槽时没有延迟.
仅供参考我如果在C中实现客户端,我会得到相同的行为:
/* by pts@fazekas.hu at Sun Apr 25 20:47:24 CEST 2010 */ #include <arpa/inet.h> #include <errno.h> #include <fcntl.h> #include <netinet/in.h> #include <stdio.h> #include <string.h> #include <sys/select.h> #include <sys/socket.h> #include <unistd.h> static int work(void) { fd_set rset; fd_set wset; fd_set eset; socklen_t sl; struct timeval timeout; struct sockaddr_in sa; int sd,i,j; long l; sd = socket(AF_INET,SOCK_STREAM,0); if (sd < 0) { perror("socket"); return 2; } l = fcntl(sd,F_GETFL,0); if (l < 0) { perror("fcntl-getfl"); close(sd); return 2; } if (0 != fcntl(sd,F_SETFL,l | O_NONBLOCK)) { perror("fcntl-setfl"); close(sd); return 2; } memset(&sa,'\0',sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(45454); sa.sin_addr.s_addr = inet_addr("127.0.0.1"); while (0 != connect(sd,(struct sockaddr*)&sa,sizeof sa)) { if (errno != EAGAIN && errno != EINPROGRESS && errno != EALREADY) { perror("connect"); close(sd); return 2; } FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset); j = 0; do { timeout.tv_sec = 0; timeout.tv_usec = 100 * 1000; /* 0.1 sec */ FD_SET(sd,&wset); FD_SET(sd,&eset); i = select(sd + 1,&rset,&wset,&eset,&timeout); if (i < 0) { perror("select"); close(sd); return 2; } if (++j == 5) { (void)write(2,"P",1); j = 0; } } while (i == 0); sl = sizeof i; if (0 != getsockopt(sd,SOL_SOCKET,SO_ERROR,&i,&sl)) { perror("getsockopt"); close(sd); return 2; } if (i != 0) { if (i == ECONNRESET) { (void)write(2,"R",1); close(sd); return -3; } fprintf(stderr,"connect-SO_ERROR: %s\n",strerror(i)); close(sd); return 2; } } close(sd); return 0; } int main(int argc,char**argv) { int i; (void)argc; (void)argv; while ((i = work()) <= 0) (void)write(2,".",1); return i; }
解决方法
鉴于睡眠和strace导致问题消失,它看起来像一些调度问题,其中服务器进程没有被安排接受连接.虽然没有在2秒的时间内安排服务器是一个非常长的时间.
也许像latencytop这样的工具可能有助于揭示正在发生的事情.你可能只能在Karmic上运行它(2.6.31),因为我认为其他内核太旧了.