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的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進行合並。具體不多說,直接上代碼

  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的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功能,文件發送功能,多服務器問題

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM