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 }