Linux之并发线程服务器

使用Linux多进程服务器,这些年虽说使用很好,但使用fork生成子进程存在一些问题。首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制等等。虽然当前采用写时拷贝(copy -on-write)技术,将真正的拷贝推迟到子进程有写操作时,但fork任然需要占用大量资源。其次,fork子进程后,需要进程间通信(IPC)在父子进程间传递信息。由于子进程从一开始就有父进程数据空间及所有描述符的拷贝,但是从子进程返回信息给父进程就需要做很多工作。
下面就介绍使用多线程实现并发服务器
#include <pthread.h>
int pthread_create( pthread_t *tid,const pthread_attr_t *attr, void*(*func)(void *), void *arg) ;
  若成功则返回0,否则返回出错编号
  返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止使用Linux多进程服务器,这些年虽说使用很好,但使用fork生成子进程存在一些问题。首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制等等。虽然当前采用写时拷贝(copy -on-write)技术,将真正的拷贝推迟到子进程有写操作时,但fork任然需要占用大量资源。其次,fork子进程后,需要进程间通信(IPC)在父子进程间传递信息。由于子进程从一开始就有父进程数据空间及所有描述符的拷贝,但是从子进程返回信息给父进程就需要做很多工作。
下面就介绍使用多线程实现并发服务器
#include <pthread.h>
int pthread_create( pthread_t *tid,const pthread_attr_t *attr, void*(*func)(void *), void *arg) ;
  若成功则返回0,否则返回出错编号
  返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于制定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。


头文件 : #include <pthread.h>  
函数定义: int pthread_join(pthread_t thread, void **retval);  
描述 :  pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。  
参数 :  thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。
  返回值 : 0代表成功。 失败,返回的则是错误号。
其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。


代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行
#include <pthread.h>
pthread_t pthread_self(vid);
函数返回调用函数的线程ID。


#inlcude <pthread.h>
void pthread_exit(void *status);
参数status指向函数的推出状态。这里的status不能指向一个局部变量,因为当前线程终止后其所有的局部变量将被撤销


还有两种方法使线程终止:
1 : 启动线程的函数pthread_create的第三个参数返回,该返回子就是线程终止的终止状态。
2 : 如果进程的main()函数返回或者任何线程调用了exit()函数进程将终止,线程将随之终止。一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。


头文件 : #include <pthread.h>  
函数定义: int pthread_join(pthread_t thread, void **retval);  
描述 :  pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。并且thread指定的线程必须是joinable的。  
参数 :  thread: 线程标识符,即线程ID,标识唯一线程。retval: 用户定义的指针,用来存储被等待线程的返回值。
  返回值 : 0代表成功。 失败,返回的则是错误号。
其实在Linux中,新建的线程并不是在原先的进程中,而是系统通过一个系统调用clone()。该系统copy了一个和原先进程完全一样的进程,并在这个进程中执行线程函数。不过这个copy过程和fork不一样。 copy后的进程和原先的进程共享了所有的变量,运行环境。这样,原先进程中的变量变动在copy后的进程中便能体现出来。
代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行
#include <pthread.h>
pthread_t pthread_self(vid);
函数返回调用函数的线程ID。
#inlcude <pthread.h>
void pthread_exit(void *status);
参数status指向函数的推出状态。这里的status不能指向一个局部变量,因为当前线程终止后其所有的局部变量将被撤销
还有两种方法使线程终止:
1 : 启动线程的函数pthread_create的第三个参数返回,该返回子就是线程终止的终止状态。

2 : 如果进程的main()函数返回或者任何线程调用了exit()函数进程将终止,线程将随之终止。


#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 1000

void process(int connfd, struct sockaddr_in client);
void *function(void *arg);
struct ARG{
	int connfd;
	struct sockaddr_in client;
};

int main()
{
	int listenfd,connfd;
	pthread_t tid;
	struct ARG *arg;
	struct sockaddr_in server;
	struct sockaddr_in client;
	if( (listenfd = socket(AF_INET, SOCK_STREAM, 0) ) == -1 )
	{
		printf("socket fail \n ");
		_exit(1);
	}

	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)SO_REUSEADDR,sizeof(SO_REUSEADDR) );
	bzero( (void *)&server,sizeof(server) );

	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr.s_addr = htonl(INADDR_ANY);           //服务器可能有许多网卡,但是INADDR_ANY 设置默认网卡IP  也可设为172.16.1.166(本人pc机上的固定IP)

	if( bind( listenfd,(struct sockaddr *)&server ,&(socklen_t){sizeof server} ) == -1 )
	{
		printf(" bind fail \n");
		_exit(1);
	}

	if( listen(listenfd, BACKLOG) < 0)
	{
		printf("listen fail  \n");
		_exit(1);
	}

	//-------------开始监听客户端-----------------------------------------------
	while(1)
	{
		//int len = sizeof(client);
		if( (connfd = accept(listenfd, (struct sockaddr *)&client, &(socklen_t){sizeof client} ) ) == -1 )   //如果接受成功,那么client结构体将会存储远程主机的各种信息
		{
			printf("accept fail \n");
			_exit(1);
		}

		arg = (struct ARG*)malloc(sizeof(struct ARG) );      //注意这里的arg是传给function函数的形参,一定需要进行内存分配,不然不能实现并发执行
		arg->connfd = connfd;
		memcpy(arg->client, &client,sizeof(client) ) ;
		if( pthread_create(tid,NULL,function,(void *)arg) )
		{	
			printf("can't create pthread \n");
			_exit(1);
		}
	}
	close(listenfd);
}

void process(int connfd, struct sockaddr_in client)
{
	int num;
	char cli_name[MAXDATASIZE];
	char databuf[MAXDATASIZE];
	printf("get data from : %s", inet_ntoa( client.sin_addr) );
	
	if((num = recv(connfd, cli_name, MAXDATASIZE, 0) ) == 0 )
	{
		printf("client disconnected \n");
		return ;
	}
	cli_name[num-1] = '\0';
	printf("client name is %s \n", cli_name);

	while( num = recv(connfd, databuf, MAXDATASIZE, 0) )
	{
		databuf[num-1] = '\0';
		printf("get message from client : %s \n",databuf);
		send(connfd, databuf, strlen(databuf), 0);
	}
	return ;
	
}

void *function(void *arg)
{
	struct ARG *info = (struct ARG*)arg;
	process(info->connfd,info->client);
	free(arg);
	pthread_exit(NULL);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值