多线程服务器

xiaoxiao2021-02-28  79

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,这样可以避免上面两者问题。

转载请注明原文地址: https://www.6miu.com/read-44604.html

最新回复(0)