用c写一个小的聊天室程序
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写一个小的聊天室程序的更多相关文章
- 利用JavaUDPSocket+多线程模拟实现一个简单的聊天室程序
对Socket的一点个人理解:Socket原意是指插座.家家户户都有五花八门的家用电器,但它们共用统一制式的插座.这样做的好处就是将所有家用电器的通电方式统一化,不需要大费周章地在墙壁上凿洞并专门接电 ...
- ASP.NET 使用application和session对象写的简单聊天室程序
ASP.Net中有两个重要的对象,一个是application对象,一个是session对象. Application:记录应用程序参数的对象,该对象用于共享应用程序级信息. Session:记录浏览 ...
- 基于websocket实现的一个简单的聊天室
本文是基于websocket写的一个简单的聊天室的例子,可以实现简单的群聊和私聊.是基于websocket的注解方式编写的.(有一个小的缺陷,如果用户名是中文,会乱码,不知如何处理,如有人知道,请告知 ...
- 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室
原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...
- 与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室
原文:与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...
- [SignalR]一个简单的聊天室
原文:[SignalR]一个简单的聊天室 1.说明 开发环境:Microsoft Visual Studio 2010 以及需要安装NuGet. 2.添加SignalR所需要的类库以及脚本文件: 3. ...
- 用ServletContext做一个简单的聊天室
这里主要是ServletContext的一个特性:ServletContext是一个公共的空间,可以被所有的客户访问.由此可见ServletContext比cookie和session的作用范围要大[ ...
- Python之小测试:用正则表达式写一个小爬虫用于保存贴吧里的所有图片
很简单的两步: 1.获取网页源代码 2.利用正则表达式提取出图片地址 3.下载 #!/usr/bin/python #coding=utf8 import re # 正则表达式 import urll ...
- 基于select的python聊天室程序
python网络编程具体参考<python select网络编程详细介绍>. 在python中,select函数是一个对底层操作系统的直接访问的接口.它用来监控sockets.files和 ...
随机推荐
- 安装并使用Oracle SQL Developer访问Oracle
---问题 如何安装并使用Oracle SQL Developer访问Oracle. ---步骤 Oracle SQL Developer是Oracle官方出品的免费图形化开发工具,相对SQL*Plu ...
- java:模拟队列操作
import java.util.LinkedList; public class Myqueue { private LinkedList<Object> linkedList; pub ...
- HDU 2276 Kiki & Little Kiki 2 矩阵构造
Kiki & Little Kiki 2 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java ...
- javascript中闭包最简单的简绍
javascript中闭包是什么 JavaScript 变量可以是局部变量或全局变量.私有变量可以用到闭包.闭包就是将函数内部和函数外部连接起来的一座桥梁. 函数的闭包使用场景:比如我们想要一个函数来 ...
- px、em、pt之间的区别与互相转换
关于px.pt和em的区别,自己有时候也会纠结到底该用什么单位,今天特意查了一些文章,下面这篇虽然很久远了,但解释的比较全面,转载收藏之.点击查看原文(原网址已失效,这是其他站点) 这里引用的是Jor ...
- 常用cmd命令大全
最早的电脑系统是从DOS系统开始,DOS时代没有现在Windows这样的视窗操作界面,让你输入命令.随着电脑的发展至今,学习一些常用cmd命令大全是很有必要.大多数的程序员高手们或计算机专家在DOS系 ...
- C# winfrom界面跳转闪烁问题解决方法
在窗体的构造函数中添加代码: SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, ...
- 细嚼慢咽C++primer(5)——顺序容器
1 顺序容器的定义 容器是容纳特定类型对象的集合. 顺序容器:将单一类型元素聚集起来成为容器,然后根据位置来存储和访问这些元素,这就是顺序容器. 标准库的三种顺序容器类型:vector, list 和 ...
- python3 邮件,多用户,抄送
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2017/8/19 10:44 # @Author : Lys # @Site : # ...
- ZT android -- 蓝牙 bluetooth (三)搜索蓝牙
android -- 蓝牙 bluetooth (三)搜索蓝牙 分类: Android的原生应用分析 2013-05-31 22:03 2192人阅读 评论(8) 收藏 举报 bluetooth蓝牙s ...