用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 }
		

复制代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • 大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相...
    y角阅读 2,378评论 2 11
  • 转自http://blog.csdn.net/xugangwen/article/details/44811783...
    扎Zn了老Fe阅读 12,685评论 1 142
  • 是谁把蓝天偷偷藏了起来, 又是谁告诉我, 天空本来就很蓝, 是谁把我的眼睛遮住, 我使劲的想推开, 可还是看不见,...
    墨笔生徽阅读 1,164评论 6 3
  • 春天的脚步近了。树上盛满了花,两周路过,有种隔世的错觉。似乎昨日还是花苞,今日就已开的灿烂。我惊异于这变化,感慨“...
    昈_阅读 211评论 0 3
  • 今天和女儿和老公一块走了娘家,女儿特高兴,喜欢姥姥家的大山,还喜欢姥姥家的大水库,苹果树……等等。女儿到姥姥家特兴...
    孙佳婧妈妈阅读 197评论 0 0