相比fork一个子进程来处理网络请求,创建一个posix线程的代价要小得多,尽管fork使用写时复制(copy on write)技术进行了优化。
另外,线程间共享内存地址空间,进行通信要比IPC简单的多,但这也带来了同步问题。
UNP卷1第3版讲解pthread_join函数指定线程id用的是pthread_t指针,但是原型在Linux上如下:
int pthread_join (pthread_t __th, void **__thread_return);
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#define MAXLINE 4096
void *do_echo(void *connfd)
{
char BUFF[MAXLINE + 1];
int n, fd = (int)connfd;
// 获取当前线程ID
pthread_t tid = pthread_self();
// 让线程转为为脱离状态
// 线程可以是joinable(默认, 可汇合的)和detached(脱离的)
// joinable状态下, 当线程运行结束, 但是没有被pthread_join时, 其所占资源不会释放
// 和detached状态下, 当线程运行结束, 其所占资源会自动释放
pthread_detach(tid);
while((n = read(fd, BUFF, MAXLINE)) > 0)
{
write(fd, BUFF, n);
BUFF[n] = '\0';
printf("[%x]: %s\n", tid, BUFF);
}
}
int main(int argc, char *argv[])
{
int listenfd, connfd, on = 1;
pthread_t tid;
socklen_t addrlen = sizeof(struct sockaddr_in), len;
struct sockaddr_in servaddr, cliaddr;
if(argc != 3)
{
printf("usage: echo <host> <port>\n");
exit(1);
}
listenfd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset(&servaddr, 0, addrlen);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(atoi(argv[2]));
inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
bind(listenfd, (struct sockaddr *)&servaddr, addrlen);
listen(listenfd, 100);
for(; ;)
{
len = addrlen;
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
// 每当一个新的连接到来, 那么创建一个线程对该连接进行处理
// 其中do_echo是线程的入口函数, connfd的指针作为参数传递
pthread_create(&tid, NULL, do_echo, (void *)connfd);
printf("create thread %x\n", tid);
}
close(listenfd);
}