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. HMTL label标签

    label标签和特定表单控件关联之后,如果用户在 label 元素内点击文本,就会触发关联的表单控件.就是说,当用户选择该label标签时,浏览器就会自动将焦点转到和label标签相关的表单控件上. ...

  2. web 开发人员必须学习的 3 门语言

    web 开发人员必须学习的 3 门语言:html  css  js HTML 定义了网页的内容 CSS 描述了网页的布局 JavaScript 网页的行为

  3. js原生日历

    突然发现日期对象可以进行 加减 , 利用这个特性写了一个可以说是对只要会JavaScript  的就可以写的日历:没有各种算法,只有一些逻辑相信只要懂javascript就差不多看俩眼就会的日历. & ...

  4. Http请求超时的一种处理方法

    URLConnection类常见的超时处理就是调用其setConnectTimeout和setReadTimeout方法: setConnectTimeout:设置连接主机超时(单位:毫秒) setR ...

  5. c++开源日志log4cplus使用开发文档

    下载地址:http://files.cnblogs.com/files/lizhigang/LOG4CPLUS%E5%BC%80%E5%8F%91%E4%B8%8E%E4%BD%BF%E7%94%A8 ...

  6. 网络I/O虚拟化,SR-IOV技术

    1.简介 网络I/O虚拟化是服务器虚拟化技术的重要组成部分,在服务器虚拟化技术领域,计算虚拟化(如CPU和内存虚拟化)已经日趋成熟,但是,网络I/O虚拟化技术的发展相对比较滞后.当前,主流的网络I/O ...

  7. 从golang-gin-realworld-example-app项目学写httpapi (三)

    https://github.com/gothinkster/golang-gin-realworld-example-app/blob/master/users/serializers.go 序列化 ...

  8. eclipse直接使用tomcat安装程序的webapp目录调试

    感谢此文:http://blog.csdn.net/soszou/article/details/23673133 本文很多技术及操作来源于此文 需求:因为微信方面的开发调试.为了测试方便,直接构建了 ...

  9. [翻译] ASCScreenBrightnessDetector

    ASCScreenBrightnessDetector ASCScreenBrightnessDetector lets you easily detect screen brightness cha ...

  10. 用ISA2006配置单网卡缓存服务器

    有些公司在部署ISA服务器之前已经有了自己的网络访问解决方案,例如通过硬件防火墙访问互联网,如果这种网络访问解决方案运行效果较好,公司就未必希望用ISA来替代当前的方案,毕竟稳定是第一位的.但在这种情 ...