用c寫一個小的聊天室程序

1.聊天室程序——客戶端

客戶端我也用了select進行I/O復(fù)用,同時監(jiān)控是否有來自socket的消息和標準輸入,近似可以完成對鍵盤的中斷使用。

其中select的監(jiān)控里,STDOUT和STDIN是已有規(guī)定的值了。

Socket_setup函數(shù)負責(zé)進行對socket進行初始化完成connect 的過程,然后在主函數(shù)里無限循環(huán)檢查sockfd和STDIN的緩沖區(qū)是否有新的消息

客戶端程序較簡單:

復(fù)制代碼
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <string.h>
 4 #include <stdbool.h>
 5 #include <unistd.h>
 6 #include <sys/socket.h>
 7 #include <arpa/inet.h>
 8 
 9 #define BUF_SIZE 256
10 #define STDIN 0
11 #define STDOUT 1
12 #define INVALID -1
13 
14 int
15 socket_setup(const char *serv_ip, int serv_port)
16 {
17     int rtn,sockfd;
18     struct sockaddr_in sockaddr;
19 
20     sockfd = socket(AF_INET, SOCK_STREAM, 0);
21     bzero(&sockaddr,sizeof(sockaddr));
22     sockaddr.sin_family = AF_INET;
23     sockaddr.sin_port = htons(serv_port);
24     inet_pton(AF_INET, serv_ip, &sockaddr.sin_addr);
25 
26     rtn = connect(sockfd,(struct sockaddr *)&sockaddr, sizeof(sockaddr));
27 
28     if (rtn == INVALID)
29     {
30         puts("connection failure\n");
31         exit(1);
32     }
33     else
34     {
35         puts("connection successful\n");
36         return sockfd;
37     }
38 }
39 
40 int
41 main(int argc, const char *argv[])
42 {
43     int i,read_size, sockfd = socket_setup(argv[1],argv[2]);
44     char buffer[BUF_SIZE];
45     fd_set fdset;
46 
47     while (1)
48     {
49         FD_ZERO(&fdset);
50         FD_SET(STDIN, &fdset);
51         FD_SET(sockfd, &fdset);
52         select(sockfd + 1, &fdset, NULL, NULL, 0);
53 
54         if( FD_ISSET( sockfd, &fdset ) )
55         {
56             readsize = read(sockfd, buffer, BUF_SIZE);
57             write(STOUT, buffer, read_size);
58 
59             if(read_size == 0)
60             {
61                 puts("server close");
62                 exit(1);
63             }
64         }
65 
66         if(FD_ISSET(STDIN, &fdset))
67         {
68             read_size = read(STDIN, buffer, BUF_SIZE);
69             write(sockfd, buffer, read_size);
70         }
71     }
72 
73     return 0;
74 }
	

復(fù)制代碼

2.聊天室程序——服務(wù)器端

我的想法是,只要建立一個數(shù)組來存放客戶端信息,每次有客戶端發(fā)送信息或者在服務(wù)器端有消息需要發(fā)出去,直接遍歷每一個元素給每個客戶端發(fā)一個就好,客戶端只需要完成以下幾件事:

1.初始化,因為select的性質(zhì),每次檢測完后會清空fdset,所以需要每一次都把所有連接上了客戶端都重新加入進fdset,涉及函數(shù)void init_clients(void),int main(int argc,const char *argv[]

2.檢測有沒有新的信息發(fā)上來了,如果有,并且可讀,那就廣播出去,涉及函數(shù):void chat(fd_set fdset),void broadcast(char *msg)

3.把所有發(fā)上來的信息按照時間格式化輸出,這里學(xué)到了幾個新函數(shù),一個是int sprintf( char *buffer, const char *format, [ argument] … );可以格式化輸出字符串和數(shù)字,一個是對世間的格式化,size_t strftime(char *strDest,size_t maxsize,const char *format,const struct tm *timeptr );這里涉及的函數(shù):void stdmsg(int i, char *buffer, const char *msg)

源程序是:

復(fù)制代碼
  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <unistd.h>
  5 #include <time.h>
  6 #include <sys/socket.h>
  7 #include <arpa/inet.h>
  8 
  9 #define TIME_SIZE 16 // 表示時間的字符串長度
 10 #define IP_SIZE 16 // IP 字符串長度
 11 #define BUF_SIZE 256 // 緩沖區(qū)大小
 12 #define CLIENT_SIZE 8 // 允許的客戶端數(shù)量
 13 #define BACKLOG CLIENT_SIZE // listen 隊列長度,等于允許的客戶端數(shù)量
 14 #define INVALID -1
 15 
 16 struct CLIENT{
 17     int clienfd;
 18     struct sockaddr_in sockaddr;
 19     char ip[IP_SIZE];
 20     int port;
 21 }clients[CLIENT_SIZE];
 22 
 23 void init_clients(void)
 24 {
 25     int i;
 26 
 27     for( i = 0; i < CLIENT_SIZE; i++ )
 28     {
 29         clients[i].clientfd = INVALID;
 30     }
 31 }
 32 
 33 void broadcast(char *msg)
 34 {
 35     int i;
 36 
 37     for(i = 0; i<CLIENT_SIZE; i++)
 38     {
 39         if( clients[i].clienfd != INVALID )
 40         {
 41             write(clients[i].clientfd, msg, sterlen(msg));
 42         }
 43     }
 44 }
 45 
 46 void stdmsg(int i, char *buffer, const char *msg)
 47 {
 48     char curtime[TIME_SIZE];
 49     time_t curtime_t;
 50     struct tm *timeinfo;
 51 
 52     curtime_t = time(NULL);
 53     timeinfo = localtime(&curtime_t);
 54     strftime(curtime, TIME_SIZE, "%X", timeinfo);
 55     sprintf(buffer,"<%s %s:%d> %s",curtime,clients[i].ip,clients[i].port,msg);
 56 }
 57 
 58 void accept_connect(int listenfd)
 59 {
 60     int connectfd,i;
 61     char buffer[BUF_SIZE];
 62     struct sockaddr_in clientaddr;
 63     socklen_t connectlen = sizeof(struct sockaddr_in);
 64 
 65     connectfd = accept( listenfd, (struct sockaddr_in *)&clientaddr, &connectlen);
 66 
 67 
 68     for( i = 0; i < CLIENT_SIZE, i++ )
 69     {
 70         if(clients[i].clienfd == INVALID)
 71         {
 72             clients[i].clienfd == connectfd;
 73             memcpy(&clients[i].sockaddr);
 74             clients[i].port = ntohs(clients[i].sockaddr.sin_port);
 75             inet_ntop(AF_INET, &clients[i].sockaddr.sin_addr, clients[i].ip, IP_SIZE);
 76             stdmsg(i,buffer,"login\n");
 77             printf("%s",buffer);
 78             broadcast(buffer);
 79             break;
 80         }
 81     }
 82 
 83     if (i == CLIENT_SIZE )
 84     {
 85         strcpy(buffer, "out of number\n");
 86         write(connectfd, buffer, strlen(buffer));
 87         close(connectfd);//所有操作利用buffer進行操作
 88     }
 89 }
 90 
 91 
 92 void chat(fd_set fdset)
 93 {
 94     int sockfd, read_size, i;
 95     char read_buf[BUF_SIZE], send_buf[BUF_SIZE];
 96 
 97     for( i = 0; i < CLIENT_SIZE; i++ )
 98     {
 99         sockfd = clients[i].clienfd;
100 
101         if(sockfd != INVALID && FD_ISSET(sockfd,&fdset))
102         {
103             read_size = read(sockfd, read_buf, BUF_SIZE - 1);
104 
105             if(read_size == 0)
106             {
107                 //connection lost
108                 close(sockfd);
109                 clients[i].clienfd = INVALID;
110                 stdmsg(i, send_buf, "logout\n");
111                 printf("%s\n",send_buf);
112                 broadcast(send_buf);
113 
114                 continue;
115             }
116             else
117             {
118                 read_buf[read_size] = '\0';
119                 stdmsg(i, send_buf, read_buf);
120                 printf("%s",send_buf);
121                 broadcast(send_buf);
122             }
123         }
124     }
125 }
126 
127 int socket_setup(int port)
128 {
129     int rtn, listenfd = socket(AF_INET, SOCK_STREAM, 0);
130     struct sockaddr_in sockaddr;
131 
132     bzero(&sockaddr, sizeof(sockaddr));
133     sockaddr.sin_family = AF_INET;
134     sockaddr.sin_port = htons(port);
135     sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
136 
137     rtn = bind(listenfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
138     if (rtn == INVALID)
139     {
140         puts("bind error\n");
141         exit(1);
142     }
143 
144     if(listen(listenfd,BACKLOG) == INVALID)
145     {
146         puts("listen error\n")
147         exit(1);
148     }
149 
150     puts("service setup\n");
151     return listenfd;
152 }
153 
154 int main(int argc,const char *argv[])
155 {
156     int maxfd, i, listenfd = socket_setup(atoi(argv[1]));
157     fdset fdset;
158 
159     init_clients();
160 
161     while(1)
162     {
163         FD_ZERO(&fdset);
164         FD_SET(listenfd, &fdset);
165         maxfd = listenfd;
166 
167         for(i = 0; i < CLIENT_SIZE; i++)
168         {
169             if(clients[i].clienfd != INVALID)
170             {
171                 FD_SET(clients[i].clienfd, &fdset);
172 
173                 if(clients[i].clienfd > maxfd)
174                 {
175                     maxfd = clients[i].clienfd;
176                 }
177             }
178         }
179 
180         select(maxfd + 1, &fdset, NULL, NULL, 0);
181 
182         if(FD_ISSET(listenfd, &fdset))
183         {
184             accept_connect(listenfd);
185         }
186         chat(fdset);
187     }
188     return 0;
189 }
		

復(fù)制代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 大綱 一.Socket簡介 二.BSD Socket編程準備 1.地址 2.端口 3.網(wǎng)絡(luò)字節(jié)序 4.半相關(guān)與全相...
    y角閱讀 2,588評論 2 11
  • 轉(zhuǎn)自http://blog.csdn.net/xugangwen/article/details/44811783...
    扎Zn了老Fe閱讀 12,840評論 1 142
  • 是誰把藍天偷偷藏了起來, 又是誰告訴我, 天空本來就很藍, 是誰把我的眼睛遮住, 我使勁的想推開, 可還是看不見,...
    墨筆生徽閱讀 1,189評論 6 3
  • 春天的腳步近了。樹上盛滿了花,兩周路過,有種隔世的錯覺。似乎昨日還是花苞,今日就已開的燦爛。我驚異于這變化,感慨“...
    昈_閱讀 225評論 0 3
  • 今天和女兒和老公一塊走了娘家,女兒特高興,喜歡姥姥家的大山,還喜歡姥姥家的大水庫,蘋果樹……等等。女兒到姥姥家特興...
    孫佳婧媽媽閱讀 214評論 0 0