上一小节我们讲了使用select来避免使用多进程的资源浪费问题。上次只是实现了从多个客户端发送数据给服务器端,接下来就要实现从服务器端发送数据给各个客户端。
使用select多路转换处理聊天程序2
client.c 使用上一节用的那个,在那个基础上修改下面几句
66 //send-recv 一些返回指没有判断,具体可以看server.c
67 if((pid=fork())[span style="color: rgba(128, 0, 128, 1)">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 //代码效果参考:http://www.zidongmutanji.com/bxxx/564212.html
}142 //FD_COPY(recvfd,servfd);
143 for(i=0;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 //代码效果参考:http://www.zidongmutanji.com/bxxx/37488.html
fgets(sendBuf,MAX_DATA_SIZE,stdin);199 for(i=0;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进行合并。具体不多说,直接上代码
client.c 基本不变
server.c
1 #include
...
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
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
177 {
178 if(fd_A【j】!=0i!=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功能,文件发送功能,多服务器问题
本文地址:
作者:无脑仔的小明
出处:
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果文中有什么错误,欢迎指出。以免更多的人被误导。有需要沟通的,可以站内私信,文章留言,或者关注“无脑仔的小明”公众号私信我。一定尽力回答。