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. 浅谈Android项目----JSON解析(4种解析技术详解)

    json简介 1.概念:json全称是javaScript object Notation,是一种并轻量级的数据交换格式. 2.特点: 1.本质就是具有特定格式的字符串 2.json完全独立于编程语言 ...

  2. SpringBoot之整合Redis

    一.SpringBoot整合单机版Redis 1.在pom.xml文件中加入redis的依赖 <dependency> <groupId>org.springframework ...

  3. JDBC的DAO设计模式

    在javaEE中,java类的属性通过getter和setter来定义,get(或set)方法去除get(set)后,首字母小写即为Java类的属性.操作java类的属性有一个工具包,BeanUtil ...

  4. EF框架CodeFirst the model backing the 'PModelEntities' context has changed since the database was created. Consider using Code First Migrations to update the database

    1.采用code first 做项目时,数据库已经生成,后期修改数据库表结构.再次运行时出现一下问题: Entity Framework : The model backing the 'Produc ...

  5. 通用CSS命名规范

    一.文件命名规范 样式文件命名主要的 master.css布局,版面 layout.css专栏 columns.css文字 font.css打印样式 print.css主题 themes.css [/ ...

  6. sql: Oracle 11g create procedure

    CREATE OR REPLACE PROCEDURE proc_Insert_BookKindList ( temTypeName nvarchar2, temParent int ) AS nco ...

  7. UOJ#400. 【CTSC2018】暴力写挂

    传送门 看到要求两棵树的 \(lca\) 深度不太好操作 考虑枚举第二棵树的 \(lca\),这样剩下的都是只和第一棵树有关的 而注意到 \(dis(x,y)=d(x)+d(y)-2d(lca(x,y ...

  8. Oracle客户端安装以及PL/SQL Developer安装方法

    1,安装Oracle客户端 2,配置数据库,如下: 安装路径:D:\app\ThinkPad\product\11.2.0\client_1\network\admin 建立文件:tnsnames.o ...

  9. Android 黑色样式menu

    效果图:

  10. ESP8266调试记录

    1.引脚图:使用STM32F103ZET6芯片的串口1  PA9-TX //PA10-RX(该串口挂载到APB2总线时钟)然后分别连接模块的RX和TX,供电使用3.3v(供电一定要稳)但不能超过5v ...