使用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的第三个参数返回,该返回子就是线程终止的终止状态。
下面就介绍使用多线程实现并发服务器
#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);
}