我只是想知道Instant Messenger和在线游戏如何如此快速地接受和传递消息。 (带套接字的网络编程)
我读到这是通过非阻塞套接字完成的。
我尝试使用pthreads阻塞套接字(每个客户端都有自己的线程),并使用kqueue
阻塞套接字,然后我使用一个程序对两个服务器进行了配置,该程序建立了99个连接(每个连接在一个线程中),然后向其中写入一些垃圾(使用1秒钟的睡眠)。设置完所有线程后,我在主线程中测量了从服务器获得连接所需的时间(带有挂钟时间)(“ 99个用户”正在写入该消息)。threads (avg): 0.000350 // only small difference to kqueuekqueue (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个连接后,服务器“卡住”了吗?)。而且,当有太多用户在写东西时,服务器将无法打开新连接。 (来自http://eradman.com/posts/kqueue-tcp.html的kqueue
服务器代码) 最佳答案
SIGPIPE
表示您正在尝试写入另一端已经关闭的套接字(或管道)(因此没有人可以读取它)。如果您对此不关心,则可以忽略SIGPIPE
信号(调用signal(SIGPIPE, SIG_IGN)
),并且信号不会有问题。当然,套接字上的write
(或send
)调用仍然会失败(使用EPIPE
),因此您需要使代码足够健壮以应对这种情况。SIGPIPE
通常会终止进程的原因是,编写太容易忽略write
/ send
调用中的错误并以100%的CPU时间运行amok的程序太容易了。只要您始终仔细检查错误并进行处理,就可以放心地忽略SIGPIPE
关于c - socket 性能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25190617/