上一小节咱们讲了使用select来避免使用多进程的资源浪费问题。上次只是实现了从多个客户端发送数据给服务器端,接下来就要实现从服务器端发送数据给各个客户端。html
使用select多路转换处理聊天程序2mysql
client.c 使用上一节用的那个,在那个基础上修改下面几句sql
66 //send-recv 一些返回指没有判断,具体能够看server.c 67 if((pid=fork())<0) 68 { 69 perror("fork error\n"); 70 } 71 else if(pid==0)/*child*/ 72 { 73 while(1) 74 { 75 fgets(sendBuf,MAX_BUF,stdin); 76 if(send(sockfd,sendBuf,strlen(sendBuf),0)==-1) 77 { 78 perror("fail to receive datas."); 79 } 80 memset(sendBuf,0,sizeof(sendBuf)); 81 } 82 } 83 else 84 { 85 while(1) 86 { 87 if((recvSize=recv(sockfd,recvBuf,MAX_BUF,0)==-1)) 88 { 89 printf("Server maybe shutdown!"); 90 break; 91 } 92 printf("Server:%s\n",recvBuf); 93 memset(recvBuf,0,sizeof(recvBuf)); 94 } 95 kill(pid,SIGKILL); 96 } 97 98 close(sockfd); 99 100 101 return 0; 102 }
server.c 咱们在上一小节的基础上加上一个处理服务器stdin读取数据,而后遍历全部fd_A里面还链接着的客户端,而后一个一个的发送数据。数据库
111 while(1) 112 { 113 FD_ZERO(&servfd);//清空全部server的fd 114 FD_ZERO(&recvfd);//清空全部client的fd 115 FD_SET(sockfd,&servfd); 116 //timeout.tv_sec=30;//能够减小判断的次数 117 switch(select(max_servfd+1,&servfd,NULL,NULL,&timeout)) 118 { ...
141 } 142 //FD_COPY(recvfd,servfd); 143 for(i=0;i<MAX_CON_NO;i++)//最大队列进行判断,优化的话,能够使用链表 144 { 145 if(fd_A[i]!=0) 146 { 147 FD_SET(fd_A[i],&recvfd); 148 } 149 } 150 151 switch(select(max_recvfd+1,&recvfd,NULL,NULL,&timeout)) 152 { ...182 } 183 184 /*用于检测stdin是否有数据*/ 185 FD_ZERO(&servfd); 186 FD_SET(STDIN_FILENO,&servfd); 187 188 switch(select(STDIN_FILENO+1,&servfd,NULL,NULL,&timeout)) 189 { 190 case -1: 191 break; 192 case 0: 193 break; 194 default: 195 /*send datas to client*/ 196 if(FD_ISSET(STDIN_FILENO,&servfd)) 197 { 198 fgets(sendBuf,MAX_DATA_SIZE,stdin); 199 for(i=0;i<MAX_CON_NO;i++) 200 { 201 if(fd_A[i]!=0) 202 { 203 printf("数据发往%d,",fd_A[i]); 204 if((sendSize=send(fd_A[i],sendBuf,strlen(sendBuf),0))!=strlen(sendBuf)) 205 { 206 perror("fail"); 207 exit(1); 208 } 209 else 210 { 211 printf("Success\n"); 212 } 213 } 214 } 215 memset(sendBuf,0,MAX_DATA_SIZE); 216 fflush(stdin); 217 } 218 break; 219 } 220 } 221 return 0; 222 }
废话很少说,直接上运行时的截图服务器
程序运行的顺序是,运行server,而后运行client1,server发送data1,client2链接server,server发送data2,client3链接server,server发送data3,此时client1退出链接,server获取fd 4 close,发送data4的时候没有发送给fd4。而后client4链接上去。分配为fd7。(虽然fd4已经关闭了,按说能够给client4分配fd4,不过太麻烦我这里就没有实现。)数据库设计
实现聊天室功能优化
接下来就是实现聊天室功能。上面server.c中有三个select,第一个是处理随时可能来的客户端链接,第二个select是处理随时可能从客户器端来的数据,第三个select是处理随时可能从控制台输入数据,并send到各个客户端。要实现聊天室功能,就对第二个和第三个select进行合并。具体很少说,直接上代码spa
client.c 基本不变设计
server.ccode
1 #include <stdio.h> ... 36 int main(int argc,char *argv[]) 37 { ... 111 while(1) 112 { 113 FD_ZERO(&servfd);//清空全部server的fd 114 FD_ZERO(&recvfd);//清空全部client的fd 115 FD_SET(sockfd,&servfd); 116 //timeout.tv_sec=30;//能够减小判断的次数 117 switch(select(max_servfd+1,&servfd,NULL,NULL,&timeout)) 118 { 119 ...
141 } ... 151 switch(select(max_recvfd+1,&recvfd,NULL,NULL,&timeout)) 152 { 153 case -1: 154 //select error 155 break; 156 case 0: 157 //timeout 158 break; 159 default: 160 for(i=0;i<conn_amount;i++) 161 { 162 if(FD_ISSET(fd_A[i],&recvfd)) 163 { 164 /*receive datas from client*/ 165 if((recvSize=recv(fd_A[i],recvBuf,MAX_DATA_SIZE,0))==-1 || recvSize==0) 166 { 167 //perror("fail to receive datas"); 168 //表示该client是关闭的 169 printf("fd %d close\n",fd_A[i]); 170 FD_CLR(fd_A[i],&recvfd); 171 fd_A[i]=0; 172 } 173 else//客户端发送数据过来,而后这里进行转发 174 { 175 /*send datas to client*/ 176 for(j=0;j<MAX_CON_NO;j++) 177 { 178 if(fd_A[j]!=0&&i!=j) 179 { 180 printf("数据发往%d,",fd_A[j]); 181 if((sendSize=send(fd_A[j],recvBuf,strlen(recvBuf),0))!=strlen(recvBuf)) 182 { 183 perror("fail"); 184 exit(1); 185 } 186 else 187 { 188 printf("Success\n"); 189 } 190 } 191 } 192 //能够判断recvBuf是否为bye来判断是否能够close 193 memset(recvBuf,0,MAX_DATA_SIZE); 194 } 195 } 196 } 197 break; 198 } 199 } 200 return 0; 201 }
直接上运行时图片
各个程序运行的循序是先运行server,而后运行client1,client2,而后cli1发送数据data1,此时cli2接收到server发来的转发数据,而后... ...
好了,到如今为止已经完成了聊天室功能了,还能够随时进入随时出来。好像还不错的样子。下一节就介绍引入用户名登录的字符界面(因为我不会图形化界面,因此就不花时间去学那个了,至于字符界面下有个curses.h听说能够弄得很好看,我这里就不弄了。有兴趣的能够去弄一下),还有一些人性化的中文提示,而后整理一下代码。因为基本没有什么技术含量,因此若是到时候篇幅不够就再加个数据库设计,考虑使用数据库来保存用户(为了方便就使用mysql吧)。
小小剧透一下,若是接下来有时间将会实现如下功能 用户功能,指令功能,私聊功能,vip功能,文件发送功能,多服务器问题