多线程并发,处理长连接或者耗时网络操作,提高并发服务效率,相对于select,epoll模型来说,理解简单,适用于单服务器简单负载。
Linux多线程并发网络编程代码
Linux多线程并发网络编程代码
/* sokcet_thread.c
编译:
gcc -g -o socket_thread socket_thread.c -lpthread
*/
点击(此处)折叠或打开
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <signal.h>
- #include <pthread.h>
- #define PORT 9999
- #define MAXSOCKFD 10
- void *process_client( void *pdata );
- void handle_sigcld(int signo);
- struct targs{
- int sockfd; // socket id
- pthread_t tid; // 线程id
- char *ip;
- int timeout;
- };
- int main( int argc, char *argv[] )
- {
- int sockfd, connfd;
- struct sockaddr_in servaddr, cliaddr;
- socklen_t cliaddr_len;
- fd_set readfds;
- char buffer[256];
- char msg[] = "Welcome to server!";
- if ( (sockfd=socket(AF_INET,SOCK_STREAM,0))<0 )
- {
- perror("socket");
- return 1;
- }
- bzero(&servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(PORT);
- servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
- int reuse = 1;
- if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
- &reuse, sizeof(int)) < 0){
- perror("setsockopt error");
- return 1;
- }
- if ( bind(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr) ) != 0 )
- {
- perror("connetc");
- return 1;
- }
- if ( listen(sockfd,5) != 0 )
- {
- perror("listen");
- return 1;
- }
- //允许中断退出
- #if 0
- signal(SIGCLD, SIG_IGN); /* now I don't have to wait()! */
- #else
- signal(SIGCLD, handle_sigcld); /* 处理中断信息*/
- #endif
- while (1)
- {
- cliaddr_len = sizeof( struct sockaddr);
- connfd = accept(sockfd, (struct sockaddr*)&cliaddr,&cliaddr_len );
- if (connfd < 0 ){
- perror("accept");
- continue;
- }
- /*新的连接,启动一个线程处理:
- 其实这里可以将线程ID保存起来,以便主进程对其进行管理,比如进程退出
- 时,对所以线程进行销毁,这里暂时不用了,简单点吧。
- 1,如果要实现可以将pthreadArgs,压入一个全局链表,对每一个线程进行管理,
- 2,如果需要传入一些全局配置信息到子线程中,可以通过pthreadArgs结构体参数传入
- 如果有些参数需要读写,还需要做线程间互斥。*/
- struct targs *pthreadArgs = (struct targs*)malloc(sizeof(struct targs) );
- pthreadArgs->sockfd = connfd;
- pthread_t pid = 0;
- int pthread = pthread_create(&pid, NULL, process_client, (void *)pthreadArgs);
- if ( pthread != 0 ){
- perror("create thread");
- }else{
- pthreadArgs->tid = pid;
- //加入全局链表,方便管理
- }
- }
- return 0;
- }
- /* 处理子线程 */
- void *process_client( void *pdata )
- {
- char *preaddata = NULL;
- struct targs *pta= (struct targs*)pdata;
- if ( pdata == NULL )
- goto EXIT_POINT;
- int connfd = pta->sockfd;
- char buffer[1024];
- printf("thread process socket:%d\n", connfd);
- /*先收4个字节长度*/
- bzero(buffer,sizeof(buffer));
- if ( read(connfd,buffer,4)<=0 ){
- goto EXIT_POINT;
- }
- /*再收内容*/
- int len = atoi(buffer);
- printf("packet length:%04d\n", len );
- if ( len <= 0 ) goto EXIT_POINT;
- preaddata = (char *)calloc(len+1, 1);
- if ( !preaddata ) goto EXIT_POINT;
- if ( read(connfd,preaddata ,len)<=0 ) {
- printf("connetc closed.\r\n");
- goto EXIT_POINT;
- }
- else
- {
- printf("recv from client:[%s]\r\n",preaddata );
- }
- #if 1 // 以下这段代码可以替换成自己的实际业务代码,比如查询数据库等。
- /*将小写转换成大写*/
- int i = 0;
- while ( i<len ){
- char c = *(preaddata+i);
- if ( c>='a' && c<='z' )
- *(preaddata+i) -= ('a'-'A');
- i++;
- }
- //模拟2秒等待时间
- sleep(2);
- #endif
- write(connfd, preaddata, len );
- printf("return client:[%s]\r\n",preaddata);
-
- EXIT_POINT:
- if ( pta ){
- close(pta->sockfd);
- free(pta);
- }
- if (preaddata) free(preaddata);
- /*释放thread,不然内存资源一直会占用不释放,造成泄漏。避免方法
- 可以在主线程pthread_join 回收,也可以通过detach分离释放*/
pthread_detach(pthread_self()); - return NULL;
- }
- void handle_sigcld(int signo)
- {
- int pid,status;
- pid = waitpid( -1, &status, 0);//-1表示等待任何子进程
- //printf("child process %d exit with %d\n",pid,status);
- }