多线程服务器

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值