用c写一个小的聊天室程序

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

客户端我也用了select进行I/O复用,同时监控是否有来自socket的消息和标准输入,近似可以完成对键盘的中断使用。

其中select的监控里,STDOUT和STDIN是已有规定的值了。

Socket_setup函数负责进行对socket进行初始化完成connect 的过程,然后在主函数里无限循环检查sockfd和STDIN的缓冲区是否有新的消息

客户端程序较简单:

 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 }

2.聊天室程序——服务器端

我的想法是,只要建立一个数组来存放客户端信息,每次有客户端发送信息或者在服务器端有消息需要发出去,直接遍历每一个元素给每个客户端发一个就好,客户端只需要完成以下几件事:

1.初始化,因为select的性质,每次检测完后会清空fdset,所以需要每一次都把所有连接上了客户端都重新加入进fdset,涉及函数void init_clients(void),int main(int argc,const char *argv[]

2.检测有没有新的信息发上来了,如果有,并且可读,那就广播出去,涉及函数:void chat(fd_set fdset),void broadcast(char *msg)

3.把所有发上来的信息按照时间格式化输出,这里学到了几个新函数,一个是int sprintf( char *buffer, const char *format, [ argument] … );可以格式化输出字符串和数字,一个是对世间的格式化,size_t strftime(char *strDest,size_t maxsize,const char *format,const struct tm *timeptr );这里涉及的函数:void stdmsg(int i, char *buffer, const char *msg)

源程序是:

  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 // 缓冲区大小
 12 #define CLIENT_SIZE 8 // 允许的客户端数量
 13 #define BACKLOG CLIENT_SIZE // listen 队列长度,等于允许的客户端数量
 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 }