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

目标:

  修改上一篇的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的指针,那么在下次sockfdclient[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函数,因为线程分为joinableunjoinable两种。

如果线程是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
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434765.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单选按钮_PerlTk教程之按钮Button、复选按钮Checkbutton、单选按钮Radiobutton(附完整代码)...

《Perl-Tk教程之按钮Button、复选按钮Checkbutton、单选按钮Radiobutton》Perl-Tk中有三种不同形式的按钮组件可供选择&#xff0c;它们分别是按钮(Button), 复选按钮(Checkbutton), 和单选按钮(Radiobutton)&#xff0c;如下图所示&#xff1a;这三种按钮看起来是不同的&#…

好奇怪呀后面加什么标点_狗狗吃饭时奇怪的小动作,你知道代表什么吗?做个懂狗的好主人...

狗狗有时候因为一些奇怪的小行为&#xff0c;会让主人觉得很可爱。如果我们希望能够了解狗狗更多一些&#xff0c;那么我们需要透过它们的行为本身&#xff0c;去理解背后所代表的含义&#xff0c;才能和狗狗更亲密的交流。很多狗狗在吃饭的时候&#xff0c;也会表现出一些奇奇…

开机未发现nvidia控制面板_修改这几个选项,就能提升你的开机速度

最近电脑非常卡&#xff0c;有时真的想把它给砸了&#xff0c;慢的自己都受不了&#xff0c;开机几分钟&#xff0c;开机完还要等上好久才能运行软件&#xff0c;都快受不了&#xff0c;要不是看在已经是10前的买的电脑&#xff0c;早就问候产商了&#xff0c;电脑缓慢的开机速…

arcgis mxt模板 创建工具条无法保存_【从零开始学GIS】ArcGIS中的绘图基本操作(二)...

大家好&#xff0c;我是肝教程肝到熊猫眼的三三。本系列教程的发布&#xff0c;受到了很多同学的鼓励&#xff0c;大家在后台或微信上表达出对教程的喜爱&#xff0c;这便是更新教程的最大动力。上回教程讲解了“GIS基本操作”、“创建文档&#xff06;加载数据”、“创建GIS数…

vivado中交织模块_搞定Markdown中的图片,一劳永逸的方法!

经常用markdown写博客的朋友一定都体会过markdown图片的蛋疼之处&#xff0c;并不是说图片的这用引用方式不好&#xff0c;而且图片要放到什么服务器上&#xff1f;以我个人为例&#xff0c;写了一篇markdown&#xff0c;想在不修改任何地方的同时适用于各种平台。刚开始使用ma…

delphi xe2 project菜单怎么没有加组件功能_交互设计:让人困惑的三大交互组件及用法...

本篇文章中&#xff0c;笔者带我们了解了三大交互组件专有名词的用法&#xff0c;并结合实际案例进行了解释说明&#xff0c;与大家分享。希望通过此文能够加深你对交互组件及其用法的理解和分析。最近有很多同学来问一些设计中组件的专业名词&#xff0c;发现大家好像都没有一…

北斗三号b1c频点带宽_北斗三号导航信号的创新设计(一)

《测绘学报》构建与学术的桥梁 拉近与权威的距离一需求与挑战1.1 卫星导航信号的作用与意义卫星导航信号是由导航卫星向地球表面播发的无线电测量信号&#xff0c;承担着传递时空基准信息和实施被动测距两大任务&#xff0c;是卫星导航系统实现三边定位的核心载体。卫星导航信号…

g++ linux 编译开栈_linux gcc和g++版本的修改

##编译的时候容易遇到&#xff1a;unsupported GNU version! gcc versions later than 6 are not supported这样的错误。所以我们要更改系统的gcc和g编译器版本。首先我们要知道一个基础的常识&#xff0c;一般而言&#xff0c;linux系统会把gcc和g默认安装在/usr/bin/的目录下…

Linux I2C核心、总线与设备驱动(一)

本章导读 I2C总线仅仅使用SCL、SDA两根信号线就实现了设备之间的数据交互&#xff0c;极大地简化对硬件资源和PCB板布线空间的占用。因此&#xff0c;I2C总线被非常广泛地应用在EEPROM、实时钟、小型LCD等设备与CPU的接口中。 Linux定义了系统的I2C驱动体系结构&#xff0c;在L…

ebay注册流程_跨境电商平台eBay企业入驻流程

整个流程一共包括8个部分了解企业入驻通道企业入驻通道将帮助现有eBay大中华卖家申请并获得高刊登额度的新账户&#xff0c;以满足卖家在品类拓展以及站点拓展的需求。准备材料1.营业执照2.法人代表身份证明&#xff0c;或eBay帐户注册人身份证明&#xff08;根据地区法规有不同…

Linux I2C核心、总线与设备驱动(二)

从上面的分析可知&#xff0c;虽然I2C硬件体系结构比较简单&#xff0c;但是I2C体系结构在Linux中的实现却相当复杂。当工程师拿到实际的电路板&#xff0c;面对复杂的 Linux I2C子系统&#xff0c;应该如何下手写驱动呢&#xff1f;究竟有哪些是需要亲自做的&#xff0c;哪些是…

mtu设置失败_Oracle RAC该调整网卡MTU值

在Oracle RAC的环境中&#xff0c;如果我们发现OSW监控数据显示包重组失败率过高&#xff0c;就需要引起足够的重视&#xff0c;因为这很可能会引发member kill/Node kill等重大故障&#xff0c;甚至在有些场景会连带影响到所有RAC节点不可用。一般我们会选择调整ipfrag相关参数…

判断字段长度大于某长度_判断数据库性能只能通过count(*)?No,这些优化方案了解一下!...

大多数用户在体验数据库时&#xff0c;接触到的最早的sql语句就是count(*)&#xff0c;因此用户判断数据库性能时通常也会通过count(*)进行比较。但在执行时通常会出现一个问题&#xff1a;对某个表做count(*)时需对全表数据进行扫描&#xff0c;当表中包含数据量较大的字段时&…

10 款基于 jQuery 的切换效果插件推荐

本文整理了 10 款非常好用的jQuery切换效果插件&#xff0c;包括平滑切换和重叠动画等&#xff0c;这些插件可以实现不同元素之间的动态切换。 1. InnerFade 这是一个基于jQuery的小插件&#xff0c;可以实现页面内的元素淡入淡出效果。 源码/演示 2. HighlightFade 该插件可以…

js矢量图类库:Raphaël—JavaScript Library

官方网址&#xff1a;http://raphaeljs.com/ Raphal is a small JavaScript library that should simplify your work with vector graphics on the web. If you want to create your own specific chart or image crop and rotate widget, for example, you can achieve it si…

gridview databind 会导致页面刷新马_Innodb批量页面刷盘情况下的quot;两次写quot;

//Innodb批量页面刷盘情况下的"两次写"//之前的文章中&#xff0c;我们介绍过innodb的两次写特性&#xff0c;这里给出链接&#xff1a;InnoDB的两次写特性今天我们完善一下这部分的内容。我们知道innodb数据页的默认大小是16kb&#xff0c;磁盘和内存通过数据页进行…

实例解析linux内核I2C体系结构(1)

作者&#xff1a;刘洪涛,华清远见嵌入式学院讲师。 一、概述 谈到在linux系统下编写I2C驱动&#xff0c;目前主要有两种方式&#xff0c;一种是把I2C设备当作一个普通的字符设备来处理&#xff0c;另一种是利用linux I2C驱动体系结构来完成。下面比较下这两种驱动。 第一种方…

★ Flex を使って Scalable Vector Graphics とビットマップを描画する

from: http://www.ibm.com/developerworks/jp/web/library/wa-svgbitmap/Flex を使って Scalable Vector Graphics とビットマップを描画するSandeep Malik, Tech Lead, IBM 概要&#xff1a; SVG (Scalable Vector Graphics) はグラフィックスの領域で最も重要な技術の 1 つで…

g5420 win7集显驱动_台式机装WIN7?雷我已经趟完了

注&#xff1a;本文只用于PC爱好者交流测试&#xff0c;文中所有测试版系统均只用于测试&#xff0c;不得用于个人或商业用途。Windows全面更新至win10版本后&#xff0c;改装Win7系统逐渐变得越来越艰难。厂商BIOS中逐渐舍弃了原始界面改为图形化&#xff0c;传统Legacy模式无…

制作完整的java可执行文件

帮教务处的老师做了一个小软件&#xff0c;所以学习了一下制作java可执行文件&#xff0c;在此分享一下。 说明&#xff1a;因为是做完很长一段时间后再截的图&#xff0c;可能有点纰漏&#xff0c;大体应该没什么问题。 我的eclipse工程文件目录: bin | images&#xff08;放图…