我只是想知道即时通讯和在线游戏如何能够如此快速地接受和传递信息。 (使用套接字进行网络编程)
我读到这是通过非阻塞套接字完成的。
我尝试使用pthreads(每个客户端获取自己的线程)阻塞套接字,使用kqueue
尝试使用非阻塞套接字。然后我使用一个创建了99个连接的程序来分析这两个服务器(在一个线程中的每个连接)然后写入一些垃圾(睡眠为1秒)。当所有线程都被设置好后,我在主线程中测量了从服务器获取连接所需的时间(挂钟时间)(而#34; 99个用户和#34;正在写入它)。
threads (avg): 0.000350 // only small difference to kqueue
kqueue (avg): 0.000300 // and this is not even stable (client side)
问题是,在使用 kqueue 进行测试时,我多次遇到SIGPIPE
错误(客户端)。 (稍微超时usleep(50)
此错误已得到修复)。我认为这是真的坏,因为服务器应该能够处理数千个连接。 (或者这是我在客户端的错吗?)关于这一点的疯狂之处是臭名昭着的pthread方法做得很好(有和没有超时)。
所以我的问题是:如何在C
中构建一个稳定套接字服务器,它可以处理数千个客户端"异步"?我只看到线程接近是一件好事,但这被认为是不好的做法。
问候
编辑:
我的测试代码:
double get_wall_time(){
struct timeval time;
if (gettimeofday(&time,NULL)){
// Handle error
return 0;
}
return (double)time.tv_sec + (double)time.tv_usec * .000001;
}
#define NTHREADS 100
volatile unsigned n_threads = 0;
volatile unsigned n_writes = 0;
pthread_mutex_t main_ready;
pthread_mutex_t stop_mtx;
volatile bool running = true;
void stop(void)
{
pthread_mutex_lock(&stop_mtx);
running = false;
pthread_mutex_unlock(&stop_mtx);
}
bool shouldRun(void)
{
bool copy;
pthread_mutex_lock(&stop_mtx);
copy = running;
pthread_mutex_unlock(&stop_mtx);
return copy;
}
#define TARGET_HOST "localhost"
#define TARGET_PORT "1336"
void *thread(void *args)
{
char tmp = 0x01;
if (__sync_add_and_fetch(&n_threads, 1) == NTHREADS) {
pthread_mutex_unlock(&main_ready);
fprintf(stderr, "All %u Threads are ready...\n", (unsigned)n_threads);
}
int fd = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) {
socket_close(fd);
fd = -1;
}
if (fd <= 0) {
fprintf(stderr, "socket_create failed\n");
}
if (write(fd, &tmp, 1) <= 0) {
fprintf(stderr, "pre-write failed\n");
}
do {
/* Write some garbage */
if (write(fd, &tmp, 1) <= 0) {
fprintf(stderr, "in-write failed\n");
break;
}
__sync_add_and_fetch(&n_writes, 1);
/* Wait some time */
usleep(500);
} while (shouldRun());
socket_close(fd);
return NULL;
}
int main(int argc, const char * argv[])
{
pthread_t threads[NTHREADS];
pthread_mutex_init(&main_ready, NULL);
pthread_mutex_lock(&main_ready);
pthread_mutex_init(&stop_mtx, NULL);
bzero((char *)&hint, sizeof(hint));
hint.ai_socktype = SOCK_STREAM;
hint.ai_family = AF_INET;
if (getaddrinfo(TARGET_HOST, TARGET_PORT, &hint, &res) != 0) {
return -1;
}
for (int i = 0; i < NTHREADS; ++i) {
pthread_create(&threads[i], NULL, thread, NULL);
}
/* wait for all threads to be set up */
pthread_mutex_lock(&main_ready);
fprintf(stderr, "Main thread is ready...\n");
{
double start, end;
int fd;
start = get_wall_time();
fd = socket(res->ai_family, SOCK_STREAM, res->ai_protocol);
if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) {
socket_close(fd);
fd = -1;
}
end = get_wall_time();
if (fd > 0) {
fprintf(stderr, "Took %f ms\n", (end - start) * 1000);
socket_close(fd);
}
}
/* Stop all running threads */
stop();
/* Waiting for termination */
for (int i = 0; i < NTHREADS; ++i) {
pthread_join(threads[i], NULL);
}
fprintf(stderr, "Performed %u successfull writes\n", (unsigned)n_writes);
/* Lol.. */
freeaddrinfo(res);
return 0;
}
SIGPIPE
在我尝试连接到kqueue
服务器时出现(在建立10个连接之后,服务器是&#34;卡住&#34;?)。当有太多用户在写东西时,服务器无法打开新连接。 (来自http://eradman.com/posts/kqueue-tcp.html的kqueue
服务器代码)
答案 0 :(得分:1)
SIGPIPE
表示您正在尝试写入另一端已经关闭的套接字(或管道)(因此没有人能够读取它)。如果您不关心这一点,您可以忽略SIGPIPE
信号(呼叫signal(SIGPIPE, SIG_IGN)
)并且信号不会出现问题。当然,套接字上的write
(或send
)调用仍然会失败(使用EPIPE
),因此您需要使代码足够强大以处理它。
SIGPIPE
通常会杀死进程的原因是它很容易编写忽略write
/ send
调用错误的程序,并且使用100%的CPU时间来运行。只要您仔细检查错误并处理它们,您就可以放心地忽略SIGPIPE
s
答案 1 :(得分:0)
或者是我的错?
这是你的错。 TCP有效。很可能你没有读过所发送的所有数据。
当有太多用户在写东西时,服务器无法打开新连接
服务器不会打开连接。客户打开连接。服务器接受连接。如果您的服务器停止这样做,那么您的接受循环有问题。它应该只做两件事:接受连接,并启动一个线程。