目标:

修改上一篇的select模式下的server,让它使用多线程来处理客户端请求(多进程的模式已经在上篇中加了注释)。

思路:

(1)服务器

我们已经在之前的客户端模型多个并发用户的过程中使用过多线程的技术了(其中还涉及到多线程利用条件变量进行线程同步),在这里我们可以很轻松的在上篇文章代码中加入线程部分代码。

//for thread

int *lptr;

pthread_t pid;

//for thread

for(i=0;i<=maxi;i++)

{

if((sockfd=client[i]) <0)

continue;

if(FD_ISSET(sockfd,&rset))

{

//thread client

lptr=(int*)malloc(sizeof(int));

*lptr=sockfd;

interrerr=pthread_create(&pid,NULL,threadPerClient,lptr);

if(errerr!=0)

printf("err%s",strerror(errno));

FD_CLR(sockfd,&allset);                                //短连接后关闭socket,服务器发不过来

client[i]=-1;

printf("canread : %d,%d,%d/n",i,sockfd,nready);

if(--nready<=0)

break;

//threadclient

}

}

但是!!!!真的那么轻松就可以加入吗?为什么我不直接在pthread_create中传入sockfd,而是要再new一个整型数然后赋值给它再传递过去呢?

答案很明显,如果直接使用sockfd的指针,那么在下次sockfd被client[i]赋值时,sockfd的值变了!而这时线程的实际操作的正是这个sockfd(不只因为线程共享变量,同时还因为传递的是指针)!所以我们复制一个新值,让线程自己处理对应的fd。

这样就完了?很明显不会那么简单,让我们看看线程处理函数就知道。

void*threadPerClient(void *arg)

{

int connfd=*((int*)arg);

free(arg);                             //防止内存泄露

pthread_detach(pthread_self());

printf("client from %d(socketnum)/n",connfd);

str_echo(connfd);

close( connfd );

return NULL;

}

看到注释了吗?“防止内存泄露“!!这是很容易发生的事,在刚才的代码中new一个新对象,那么我们就必须在使用后释放。除此以外我们还注意到这里调用了pthread_detach函数,因为线程分为joinable和unjoinable两种。

如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K多)。只有当你调用了pthread_join之后这些资源才会被释放。若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
    unjoinable属性可以在pthread_create时指定,或在线程创建后在线程中pthread_detach自己, 如:pthread_detach(pthread_self()),将状态改为unjoinable状态,确保资源的释放。或者将线程置为 joinable,然后适时调用pthread_join.
(其实就类似于进程退出时父亲进程的wait)

(2)客户端

无需修改

代码:

server.cpp

  1 #include<sys/types.h>
  2 #include<sys/socket.h>
  3 #include<strings.h>
  4 #include<arpa/inet.h>
  5 #include<unistd.h>
  6 #include<stdlib.h>
  7 #include<stdio.h>
  8 #include<string.h>
  9 #include<errno.h>
 10 #include<signal.h>
 11 #include<sys/wait.h>
 12 #include<sys/time.h>
 13 #include<pthread.h>
 14
 15 #define LISTEN_PORT 84
 16
 17 //服务器处理
 18 void str_echo(int sockfd)            // 服务器收到客户端的消息后的响应
 19 {
 20     ssize_t n;
 21 char line[512];
 22
 23     printf("ready to read/n");
 24
 25 while( (n=read(sockfd,line,512))>0 )      //阻塞IO版本
 26     {
 27             line[n]='/0';
 28             printf("Client Diary: %s/n",line);
 29
 30 char msgBack[512];
 31             snprintf(msgBack,sizeof(msgBack),"recv: %s/n",line);
 32             write(sockfd,msgBack,strlen(msgBack));
 33             bzero(&line,sizeof(line));
 34     }
 35     printf("end read/n");
 36 }
 37
 38 //子进程结束的信号处理
 39 float timeuse;
 40 void sig_child(int signo)         //父进程对子进程结束的信号处理
 41 {
 42     pid_t pid;
 43 int   stat;
 44
 45 struct timeval tpstart,tpend;
 46 //float timeuse;
 47     gettimeofday(&tpstart,NULL);
 48
 49 while( (pid=waitpid(-1,&stat,WNOHANG))>0)
 50     printf("child %d terminated/n",pid);
 51
 52     gettimeofday(&tpend,NULL);
 53
 54     timeuse+=1000000*(tpend.tv_sec-tpstart.tv_sec)+tpend.tv_usec-tpstart.tv_usec;
 55 //timeuse/=1000000;
 56     printf("Use Time:%f/n",timeuse/1000000);
 57 return;
 58 }
 59
 60
 61
 62 //使用线程代替进程来处理客户请求
 63 void* threadPerClient(void*arg)
 64 {
 65 int connfd=*((int*)arg);
 66     free(arg);                //防止内存泄露
 67     pthread_detach(pthread_self());
 68
 69     printf("client from %d(socket num)/n",connfd);
 70     str_echo(connfd);
 71
 72     close( connfd );
 73 return NULL;
 74 }
 75
 76
 77 int main(int argc, char**argv)
 78 {
 79 int listenfd, connfd;
 80     pid_t childpid;
 81     socklen_t chilen;
 82
 83 struct sockaddr_in chiaddr,servaddr;
 84
 85 //for select
 86 int i,maxi,maxfd,sockfd;
 87 int nready,client[FD_SETSIZE];
 88     ssize_t n;
 89     fd_set rset,allset;
 90 //for select
 91
 92     listenfd=socket(AF_INET,SOCK_STREAM,0);
 93 if(listenfd==-1)
 94     {
 95         printf("socket established error: %s/n",(char*)strerror(errno));
 96     }
 97
 98     bzero(&servaddr,sizeof(servaddr));
 99     servaddr.sin_family=AF_INET;
100     servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
101     servaddr.sin_port=htons(LISTEN_PORT);
102
103 int bindc=bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
104 if(bindc==-1)
105     {
106         printf("bind error: %s/n",strerror(errno));
107     }
108
109     listen(listenfd,SOMAXCONN);               //limit是SOMAXCONN
110
111 //initial "select" elements
112     maxfd=listenfd;            //新增listenfd,所以更新当前的最大fd
113     maxi=-1;
114 for(i=0;i<FD_SETSIZE;i++)
115       client[i] =-1;
116     FD_ZERO(&allset);
117     FD_SET(listenfd,&allset);
118 //end initial
119
120 int cliconn=0;
121     signal(SIGCHLD,sig_child);
122 for(;;)
123     {
124         rset=allset;
125         nready=select(maxfd+1,&rset,NULL,NULL,NULL); //一开始select监听的是监听口
126 //如果有timeout设置,那么每次select之前都要再重新设置一下timeout的值
127 //因为select会修改timeout的值。
128 if(FD_ISSET(listenfd,&rset))
129         {
130             chilen=sizeof(chiaddr);
131
132             connfd=accept(listenfd,(struct sockaddr*)&chiaddr,&chilen);
133 //阻塞在accept,直到三次握手成功了才返回
134 if(connfd==-1)
135             printf("accept client error: %s/n",strerror(errno));
136 else
137             printf("client connected:%d/n",++cliconn);
138
139 for(i=0;i<FD_SETSIZE;i++)
140             {
141 if (client[i]<0)
142                 {
143                     client[i]=connfd;    //找一个最小的插进入
144 break;
145                 }
146             }
147 if(i==FD_SETSIZE)
148             {
149                 printf("too many clients/n");
150                 exit(0);
151             }
152             FD_SET(connfd,&allset);   //新加入的描述符,还没判断是否可以或者写,所以后面使用rset而不是allset
153
154
155
156 if(connfd>maxfd)
157               maxfd=connfd;
158 if(i>maxi)
159               maxi=i;
160 if(--nready<=0)
161 continue;
162         }
163
164 //for thread
165 int*lptr;
166         pthread_t pid;
167 //for thread
168
169 for(i=0;i<=maxi;i++)
170         {
171 if( (sockfd=client[i]) <0)
172 continue;
173 if(FD_ISSET(sockfd,&rset))
174             {
175 //thread client
176                 lptr=(int*)malloc(sizeof(int));
177 *lptr=sockfd;
178 //pthread_t pid;
179
180 int errerr=pthread_create(&pid,NULL,threadPerClient,lptr);
181 if(errerr!=0)
182                 printf("err %s",strerror(errno));
183
184                 FD_CLR(sockfd,&allset);                                //短连接后关闭socket,服务器发不过来
185                 client[i]=-1;
186
187                 printf("can read : %d,%d,%d/n",i,sockfd,nready);
188 if(--nready<=0)
189 break;
190 //thread client
191
192             }
193         }
194
195     }
196 }

作者: Aga.J
出处: http://www.cnblogs.com/aga-j
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Linux TCP server系列(6)-select模式下的多线程server相关推荐

  1. Linux TCP server系列(5)-select模式下的单进程server

    目标:  让服务器退化为单进程模式,但是利用select来提升性能 思路:     (1)服务器        传统的单进程服务器一旦accept了客户端的TCP连接后,就转入客户请求的处理,处理完成 ...

  2. 在单用户模式下启动SQL Server的不同方法

    In this article, we will review different ways to start SQL Server in single user mode. 在本文中,我们将介绍在单 ...

  3. js监听select值变化_网络编程——C++实现socket通信(TCP)高并发之select模式

    相关函数: 服务端: socket() bind() listen() FD_ZERO()等辅助函数 select() 高并发select模式 accept() read() 或 recv()等 wr ...

  4. linux efi 双系统,EFI+GPT模式下Linux与Windows双系统要诀

    本文并非要对 BIOS/EFI/MBR/GPT 等进行理论探讨,相关知识请各位自行搜索学习.本着薄荷网一贯坚持的实操原则,本文主要是介绍在"EFI引导+GPT分区"模式下,安装 L ...

  5. linux 返回非法指令,linux – ARM Cortex A7在内核模式下返回PMCCNTR = 0,在用户模式下返回非法指令(即使在PMUSERENR = 1之后)...

    我想在Raspberry Pi 2上读取循环计数寄存器(PMCCNTR),它有一个ARM Cortex A7内核.我为它编译了一个内核模块,如下所示: #include #include int in ...

  6. 用uefi安装linux系统安装win7系统分区,UEFI模式下Win/Linux双系统安装

    自从Linux阵营的Ubuntu异军突起之后,双系统的安装一直是简单友好的.先装Windows再装Linux,只要新分区(挂载点 Mount point)的选择没出问题,多系统的Grub启动菜单就会在 ...

  7. linux vi模式替换,linux基础命令之:vi模式下查找和替换

    一.查找 查找命令 /pattern :向下查找pattern匹配字符串 ?pattern:向上查找pattern匹配字符串 使用了查找命令之后,使用如下两个键快速查找: n:按照同一方向继续查找 N ...

  8. linux网卡主备,linux网卡bounding的主备模式下上层路由端需要什么设置?

    不需要做路由设置,给你个列子: # cat /etc/sysconfig/network-scripts/ifcfg-bond0 DEVICE=bond0 BOOTPROTO=none ONBOOT= ...

  9. sql server 锁与事务拨云见日(下)

    sql server 锁与事务拨云见日(下) 原文:sql server 锁与事务拨云见日(下) 在锁与事务系列里已经写完了上篇中篇,这次写完下篇.这个系列俺自认为是有条不紊的进行,但感觉锁与事务还是 ...

最新文章

  1. 透过腾讯张潼离职事件,看AI研究院如何才算成功?
  2. Java8 之 lambda 表达式、方法引用、函数式接口、默认方式、静态方法
  3. BS4xpath的使用
  4. 别再叫我“老工”!!!工程师姓什么很重要!
  5. linux 后台计算,科学网-如何在Linux中做批处理和后台计算-张彦的博文
  6. 通过 PL/SQL Developer 建表
  7. java如何对List集合中的元素进行排序(请收藏)
  8. javascript动画系列 —— 切换图片(原生)
  9. 程序员的职业素养---转载
  10. Java JSch 远程执行 Shell 命令
  11. EM最大期望算法与jensen不等式
  12. smartsvn基本操作
  13. cad化气路图_气路图符号大全
  14. 机器学习原理与实践(Python版)
  15. 进程间通讯的5种方式
  16. 【FFmpeg】使用 ffmpeg 软件让视频旋转适当角度(亲测有效)
  17. 同时收到多家公司offer,怎样选择?
  18. 勒索病毒肆虐的原因分析
  19. 生成二维码及微信长按识别二维码
  20. python刷新cdn_使用Python解析阿里云CDN日志

热门文章

  1. mysql key value 引擎_mysql集成的key-value引擎-个人翻译
  2. sql的子查询超级慢-子查询酿的祸
  3. mysql host %s_python mysql:虽然%s和列匹配,但并非SQL语句中使用的所有参数
  4. Pycharm中运行Python代码的几种方式
  5. JDBC_设计架构_驱动类加载_建立Connection_效率测试
  6. spring----06 更多DI知识
  7. 关于springMVC传参问题
  8. hdoj-3342-Legal or Not(拓扑排序)
  9. 设计模式之 里氏替换原则
  10. 《DSP using MATLAB》示例Example7.25