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

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

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

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

客户端程序较简单:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h> #define BUF_SIZE 256
#define STDIN 0
#define STDOUT 1
#define INVALID -1 int
socket_setup(const char *serv_ip, int serv_port)
{
int rtn,sockfd;
struct sockaddr_in sockaddr; sockfd = socket(AF_INET, SOCK_STREAM, );
bzero(&sockaddr,sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(serv_port);
inet_pton(AF_INET, serv_ip, &sockaddr.sin_addr); rtn = connect(sockfd,(struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (rtn == INVALID)
{
puts("connection failure\n");
exit();
}
else
{
puts("connection successful\n");
return sockfd;
}
} int
main(int argc, const char *argv[])
{
int i,read_size, sockfd = socket_setup(argv[],argv[]);
char buffer[BUF_SIZE];
fd_set fdset; while ()
{
FD_ZERO(&fdset);
FD_SET(STDIN, &fdset);
FD_SET(sockfd, &fdset);
select(sockfd + , &fdset, NULL, NULL, ); if( FD_ISSET( sockfd, &fdset ) )
{
readsize = read(sockfd, buffer, BUF_SIZE);
write(STOUT, buffer, read_size); if(read_size == )
{
puts("server close");
exit();
}
} if(FD_ISSET(STDIN, &fdset))
{
read_size = read(STDIN, buffer, BUF_SIZE);
write(sockfd, buffer, read_size);
}
} return ;
}

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)

源程序是:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h> #define TIME_SIZE 16 // 表示时间的字符串长度
#define IP_SIZE 16 // IP 字符串长度
#define BUF_SIZE 256 // 缓冲区大小
#define CLIENT_SIZE 8 // 允许的客户端数量
#define BACKLOG CLIENT_SIZE // listen 队列长度,等于允许的客户端数量
#define INVALID -1 struct CLIENT{
int clienfd;
struct sockaddr_in sockaddr;
char ip[IP_SIZE];
int port;
}clients[CLIENT_SIZE]; void init_clients(void)
{
int i; for( i = ; i < CLIENT_SIZE; i++ )
{
clients[i].clientfd = INVALID;
}
} void broadcast(char *msg)
{
int i; for(i = ; i<CLIENT_SIZE; i++)
{
if( clients[i].clienfd != INVALID )
{
write(clients[i].clientfd, msg, sterlen(msg));
}
}
} void stdmsg(int i, char *buffer, const char *msg)
{
char curtime[TIME_SIZE];
time_t curtime_t;
struct tm *timeinfo; curtime_t = time(NULL);
timeinfo = localtime(&curtime_t);
strftime(curtime, TIME_SIZE, "%X", timeinfo);
sprintf(buffer,"<%s %s:%d> %s",curtime,clients[i].ip,clients[i].port,msg);
} void accept_connect(int listenfd)
{
int connectfd,i;
char buffer[BUF_SIZE];
struct sockaddr_in clientaddr;
socklen_t connectlen = sizeof(struct sockaddr_in); connectfd = accept( listenfd, (struct sockaddr_in *)&clientaddr, &connectlen); for( i = ; i < CLIENT_SIZE, i++ )
{
if(clients[i].clienfd == INVALID)
{
clients[i].clienfd == connectfd;
memcpy(&clients[i].sockaddr);
clients[i].port = ntohs(clients[i].sockaddr.sin_port);
inet_ntop(AF_INET, &clients[i].sockaddr.sin_addr, clients[i].ip, IP_SIZE);
stdmsg(i,buffer,"login\n");
printf("%s",buffer);
broadcast(buffer);
break;
}
} if (i == CLIENT_SIZE )
{
strcpy(buffer, "out of number\n");
write(connectfd, buffer, strlen(buffer));
close(connectfd);//所有操作利用buffer进行操作
}
} void chat(fd_set fdset)
{
int sockfd, read_size, i;
char read_buf[BUF_SIZE], send_buf[BUF_SIZE]; for( i = ; i < CLIENT_SIZE; i++ )
{
sockfd = clients[i].clienfd; if(sockfd != INVALID && FD_ISSET(sockfd,&fdset))
{
read_size = read(sockfd, read_buf, BUF_SIZE - ); if(read_size == )
{
//connection lost
close(sockfd);
clients[i].clienfd = INVALID;
stdmsg(i, send_buf, "logout\n");
printf("%s\n",send_buf);
broadcast(send_buf); continue;
}
else
{
read_buf[read_size] = '\0';
stdmsg(i, send_buf, read_buf);
printf("%s",send_buf);
broadcast(send_buf);
}
}
}
} int socket_setup(int port)
{
int rtn, listenfd = socket(AF_INET, SOCK_STREAM, );
struct sockaddr_in sockaddr; bzero(&sockaddr, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); rtn = bind(listenfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
if (rtn == INVALID)
{
puts("bind error\n");
exit();
} if(listen(listenfd,BACKLOG) == INVALID)
{
puts("listen error\n")
exit();
} puts("service setup\n");
return listenfd;
} int main(int argc,const char *argv[])
{
int maxfd, i, listenfd = socket_setup(atoi(argv[]));
fdset fdset; init_clients(); while()
{
FD_ZERO(&fdset);
FD_SET(listenfd, &fdset);
maxfd = listenfd; for(i = ; i < CLIENT_SIZE; i++)
{
if(clients[i].clienfd != INVALID)
{
FD_SET(clients[i].clienfd, &fdset); if(clients[i].clienfd > maxfd)
{
maxfd = clients[i].clienfd;
}
}
} select(maxfd + , &fdset, NULL, NULL, ); if(FD_ISSET(listenfd, &fdset))
{
accept_connect(listenfd);
}
chat(fdset);
}
return ;
}

用c写一个小的聊天室程序的更多相关文章

  1. 利用JavaUDPSocket+多线程模拟实现一个简单的聊天室程序

    对Socket的一点个人理解:Socket原意是指插座.家家户户都有五花八门的家用电器,但它们共用统一制式的插座.这样做的好处就是将所有家用电器的通电方式统一化,不需要大费周章地在墙壁上凿洞并专门接电 ...

  2. ASP.NET 使用application和session对象写的简单聊天室程序

    ASP.Net中有两个重要的对象,一个是application对象,一个是session对象. Application:记录应用程序参数的对象,该对象用于共享应用程序级信息. Session:记录浏览 ...

  3. 基于websocket实现的一个简单的聊天室

    本文是基于websocket写的一个简单的聊天室的例子,可以实现简单的群聊和私聊.是基于websocket的注解方式编写的.(有一个小的缺陷,如果用户名是中文,会乱码,不知如何处理,如有人知道,请告知 ...

  4. 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室

    原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

  5. 与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室

    原文:与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

  6. [SignalR]一个简单的聊天室

    原文:[SignalR]一个简单的聊天室 1.说明 开发环境:Microsoft Visual Studio 2010 以及需要安装NuGet. 2.添加SignalR所需要的类库以及脚本文件: 3. ...

  7. 用ServletContext做一个简单的聊天室

    这里主要是ServletContext的一个特性:ServletContext是一个公共的空间,可以被所有的客户访问.由此可见ServletContext比cookie和session的作用范围要大[ ...

  8. Python之小测试:用正则表达式写一个小爬虫用于保存贴吧里的所有图片

    很简单的两步: 1.获取网页源代码 2.利用正则表达式提取出图片地址 3.下载 #!/usr/bin/python #coding=utf8 import re # 正则表达式 import urll ...

  9. 基于select的python聊天室程序

    python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...

随机推荐

  1. oracle lpad rpad函数

    学习并记录 1.情况一 ) from dual; 运行结果如下: email长度5,默认添加3个空格在左边 2.情况二 ) from dual; 运行结果如下: email长度5,截取2两个字符 3. ...

  2. [LeetCode]Delete and Earn题解(动态规划)

    Delete and Earn Given an array nums of integers, you can perform operations on the array. In each op ...

  3. Java自定义cas操作

    java Unsafe工具类提供了一个方法 public final native boolean compareAndSwapObject(Object var1, long var2, Objec ...

  4. SQL修改表结构

    --(1)向数据库Student表中添加Name字段 use MR_NXT alter table student add Name char(20) --(2)将Student表中Name的字段的数 ...

  5. 理解webpack4.splitChunks之cacheGroups

    cacheGroups其实是splitChunks里面最核心的配置,一开始我还认为cacheGroups是可有可无的,这是完全错误的,splitChunks就是根据cacheGroups去拆分模块的, ...

  6. MathQuill.js

    MathQuill.js通过html.css.javascript实现数学公式 <p>Type math here: <span id="math-field"& ...

  7. Selenium+java项目测试问题整理

    一.页面跳转到另一链接 问题描述:打开页面链接为A.com,但是页面元素需跳转到链接B.com.这时B页面将无法识别该元素,导致拨错 解决方案:重新自定义驱动,打开新链接 (PS:比较笨的解决方法,但 ...

  8. js作用域链以及全局变量和局部变量

    > [带var] > 在当前作用于中声明了一个变量,如果当前是全局作用域,也相当于给全局作用域设置了一个属性叫做a ```javascript //=>变量提升:var a; < ...

  9. 使用mybatis plus自动生成controller、service、dao、mapper、entity代码

    官网:http://mp.baomidou.com(这个项目不仅仅可以用于代码生成,还有分页等其他功能,是对mybatis的一层封装) 要求:基于sql自动生成domain.controller.se ...

  10. 下面有关css选择器的说法错误的是?

    下面有关css选择器的说法错误的是? .intro --- 选择 class="intro" 的所有元素. #firstname --- 选择 id="firstname ...