我试图测量一个正在写的TCP服务器的速度,我注意到可能有一个测量connect()调用速度的根本问题:如果我以非阻塞的方式连接,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, socket.SOCK_STREAM, 0) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 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()操作是快速的,但是在每隔几秒钟内,恰好有一个连接()操作需要至less2秒(如输出上连续的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
没有延迟。
很奇怪,如果我取消快速的time.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_UNIXsockets,则不会有延迟。
仅供参考如果我在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)上运行它,因为其他内核太旧我认为。
你确定这是connect()
调用慢的吗? 在大多数图书馆中,DNS解析始终是阻塞的。 检查是否总是使用IP地址有什么区别。