多线程服务器

来源:互联网 发布:远程协助软件 编辑:程序博客网 时间:2024/05/16 13:06

Posix线程库:
a) 与线程有关的函数都构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的。。
b) 要使用这些库函数,要引入头文件<pthread.h>。
c) 链接这些线程库函数时要使用编译命令的”-lpthread”选项。
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void*(*start_toutine)(void*),void *arg)
功能:创建一个新的线程
参数:
a) thread:返回线程ID
b) attr:设置线程的属性,attr为NULL表示使用默认属性
c) start_routine:是个函数指针,线程启动时要执行的函数
d) arg:传给线程启动函数的参数
返回值:成功返回0,失败返回错误码。
错误码:
a) 传统的函数,成功返回0,失败返回-1,并且对全局变量errno赋值以错误指示。
b) pthreads函数出错时不会设置全局变量errno,而是将错误码通过返回值返回。
c) pthread同样也提供了县城内的errno变量,以支持其使用errno代码。对于threads函数的错误,建议通过返回值来判定,因为读取返回值要比读取线程内的errno变量的开销更小。
int pthread_join(pthread_t thread,void **retval)
功能:回收创建的进程
参数:thread,被回收线程的ID,线程退出时的状态,不关心的话可以设为NULL。
返回值:成功返回0,失败返回错误码。
说明:如果未调用该函数,将会出现僵尸线程,当然可以把该线程设置为分离态,由内核负责回收。
void pthread_exit(void *retval);
功能:退出本线程,注意不能使用exit,任意一个线程调用exit,都会导致进程的退出。
参数:retval记录线程退出时的状态,不关心可设置为NULL。
int pthread_cancel(pthread_t thread)
功能:取消一个执行中的线程。
参数:thread,线程ID。
返回值:成功返回0,失败返回错误码。
int pthread_detach(pthread_t thread)
功能:将该线程分离,其回收由内核负责。
参数:thread,线程ID
返回值:成功返回0,失败返回错误码。

#include<pthread.h>#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/wait.h>#include<stdlib.h>#include<stdio.h>#include<errno.h>#include<string.h>#include<signal.h>#define ERR_EXIT(m) do{perror(m);exit(EXIT_FAILURE);}while(0)void* do_service(void *arg){char recvbuf[1024];//int connfd = *(int*)arg;    int connfd=(long)arg;pthread_detach(pthread_self());//将该线程设置为分离态,由内核负责回收后while (1){memset(&recvbuf, 0, sizeof(recvbuf));int ret = read(connfd, recvbuf, sizeof(recvbuf));if (ret == -1){ERR_EXIT("read");}else if (ret == 0){printf("client cloase\n");break;}fputs(recvbuf, stdout);write(connfd, recvbuf, strlen(recvbuf));}    close(connfd);//关闭套接字return NULL;}void handle_sigchild(int sig){//  wait(NULL);   while(waitpid(-1,NULL,WNOHANG)>0);//}int main(void){//signal(SIGCHLD, SIG_IGN);//signal(SIGCHLD,handle_sigchild);    int listenfd;if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)ERR_EXIT("socket");struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(5188);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);int on = 1;if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)ERR_EXIT("setsockopt");if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)ERR_EXIT("bind");if (listen(listenfd, SOMAXCONN) < 0)ERR_EXIT("listten");while (1){int connfd;struct sockaddr_in peeraddr;socklen_t peerlen = sizeof(peeraddr);if ((connfd = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)ERR_EXIT("accept");printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));        pthread_t tid;        int ret=pthread_create(&tid,NULL,do_service,(void*)((long)connfd));        if(ret!=0)ERR_EXIT("creat");}close(listenfd);}
细节问题:
a) 因为服务器主线程只负责监听套接口,不负责回收创建的线程,因此创建的线程需要设置为分离态,由内核负责回收,否则会出现僵尸线程。
b) 创建的线程要自己关闭套接口,主线程不负责这项工作。
c) pthre_create函数传参数,有三种做法:第一,pthread_create(&tid,NULL,do_service,(void *)connfd),这种做法不可移植,因为在64位操作系统里,地址是8位,而int是4位, 这么做会出问题,需要将connfd转成8位的long类型程序才能正确运行。第二,pthread_create(&tid,NULL,do_service,(void *(&connfd)),这个做法会出问题,如果同时到达多个连接,由于是取的是地址,因此他的值可能在新的线程里改变,出现错误。第三,使用int *p=(int*)malloc(sizeof(int)),*p=connfd,这样可以避免上面两者问题。