首页 > 代码库 > Socket网络编程--聊天程序(5)

Socket网络编程--聊天程序(5)

  上一小节我们讲了使用select来避免使用多进程的资源浪费问题。上次只是实现了从多个客户端发送数据给服务器端,接下来就要实现从服务器端发送数据给各个服务器。

 

  使用select多路转换处理聊天程序2

  client.c 使用上一节用的那个,在那个基础上修改下面几句

 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的fd114         FD_ZERO(&recvfd);//清空所有client的fd115         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 else210 {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 <stdio.h>   ... 36 int main(int argc,char *argv[]) 37 {    ...111     while(1)112     {113         FD_ZERO(&servfd);//清空所有server的fd114         FD_ZERO(&recvfd);//清空所有client的fd115         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 error155 break;156 case 0:157 //timeout158 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 else187 {188 printf("Success\n");189 }190 }191 }192 //可以判断recvBuf是否为bye来判断是否可以close193 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功能,文件发送功能,多服务器问题

  本文地址: http://www.cnblogs.com/wunaozai/p/3871563.html