C写的
要求:
用户默认处于广播模式,一个客户在其客户端发送的消息,其它客户端用户全部可以收到;
程序支持下列命令
/help:显示帮助信息(思考:信息是放在客户端还是服务器端);
/quit:用户退出聊天室,同时将退出信息广播给其他用户;
/who:显示在线用户;
/send 用户名 消息:向指定用户发送点到点消息
chatserver.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netdb.h>
#include<sys/time.h>
#include<sys/types.h>
#define PORT 1573
#define BACKLOG 10
#define BUFSIZE 2048
//定义一个结构体,使得客户的信息可以结合到一起
struct client_info{
int id; //表示用户现在接入的套接字
char name[256];
int first; //表示用户是不是第一次访问,用于传入名字
};
int main(){
fd_set allset; //需要扫描的所有套接字
fd_set rset; //select过后的套接字
struct sockaddr_in server;
struct sockaddr_in client;
int maxfd;
int sockfd;
int confd;
char recvbuf[BUFSIZE];
char sendbuf[BUFSIZE];
int recvnum;
int sendnum;
int opt; //定义套接字属性
int length; //用于connect函数
opt = SO_REUSEADDR;
length = sizeof(struct sockaddr);
int tmp_i;
int tmp_j;
char str1[256];
char str2[256];
char str3[256];
int tmpid=-1; //用于进行实际处理的套接字,在关系上是通过tmpfd得到的
int tmpfd=-1; //用来在新一轮循环中替换掉tmp_i,使得tmp_i的信息可以保存
struct client_info clientinfo[BACKLOG];
//初始化套接字集合
FD_ZERO(&allset);
FD_ZERO(&rset);
if(-1 == (sockfd=socket(AF_INET,SOCK_STREAM,0)))
{
perror("create socket error!\n");
exit(1);
}
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
memset(&server,0,sizeof(server));
memset(sendbuf,0,BUFSIZE);
memset(recvbuf,0,BUFSIZE);
int i;
//初始化客户的信息
for(i=0;i<BACKLOG;i++)
{
clientinfo[i].id = -1;
clientinfo[i].name[0] = '\0';
clientinfo[i].first = -1;
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(PORT);
if(-1 == bind(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr)))
{
perror("bind socket error!\n");
exit(1);
}
if(-1 == listen(sockfd,BACKLOG))
{
perror("listen error!\n");
exit(1);
}
FD_SET(sockfd,&allset);
maxfd = sockfd;
printf("server is ok!\n");
while(1)
{
rset = allset;
if(-1 == select(maxfd+1,&rset,NULL,NULL,NULL))
{
perror("select function error!\n");
exit(1);
}
for(tmp_i = sockfd;tmp_i <= maxfd;tmp_i++)
{
//处理:如果是监听套接字被激活
if(FD_ISSET(tmp_i,&rset))
{
if(tmp_i == sockfd)
{
confd = accept(sockfd,(struct sockaddr*)&client,&length);
if(confd == -1)
{
perror("accept error!\n");
exit(1);
}
clientinfo[confd].id = confd;
clientinfo[confd].first = 1; //将first置为1,用于第一个接收包的名字
FD_SET(confd,&allset);
if(confd > maxfd)
maxfd = confd;
}
else{
//处理:如果是连接套接字被激活
recvnum = read(tmp_i,recvbuf,sizeof(recvbuf));
if(clientinfo[tmp_i].first == 1) //由上,得到客户的名字
{
strcpy(clientinfo[tmp_i].name,recvbuf);
clientinfo[tmp_i].first = -1;
}
if(0>recvnum)
{
perror("recieve error!\n");
exit(1);
}
if(recvbuf[0]=='/')
{
//处理:以‘/’开始的接收包表示现在是指令
if(strcmp(recvbuf,"/who\n")==0){
//请求现在有哪些用户在线
for(tmpfd = sockfd;tmpfd<=maxfd;tmpfd++)
{
if(FD_ISSET(tmpfd,&allset))
strcat(sendbuf,clientinfo[tmpfd].name);
}
//因为只是当前输入“/who”指令的用户想要知道谁在线
//只把内容返回给他/她,用continue重新新的一轮循环
write(tmp_i,sendbuf,sizeof(sendbuf));
continue;
}
if(strcmp(recvbuf,"/quit\n")==0)
{
//当前客户请求退出
printf("client:%s exit!\n",clientinfo[tmp_i].name);
FD_CLR(tmp_i,&allset);
close(tmp_i);
strcat(sendbuf,clientinfo[tmp_i].name);
strcat(sendbuf," was exit!");
}
//初始化字符串,用于分别存储/send usr msg中的各个部分
memset(str1,0,sizeof(str1));
memset(str2,0,sizeof(str2));
memset(str3,0,sizeof(str3));
sscanf(recvbuf,"%s %s %s",str1,str2,str3);
strcat(str2,"\n");
if(strcmp(str1,"/send")==0)
{
tmpid = -1; //以防在新的循环中tmpid的值被上一次循环所改变
int j = 0;
for(tmpfd = sockfd;tmpfd<=maxfd;tmpfd++)
{
//查询到指定名字下的客户的套接字
if(FD_ISSET(tmpfd,&allset))
{
if(strcmp(str2,clientinfo[tmpfd].name)==0)
tmpid = tmpfd;
}
}
if(tmpid==-1)
{
//表示并没有当前客户与之匹配,返回消息给发送端
strcat(sendbuf,"user isn't online!");
write(tmp_i,sendbuf,sizeof(sendbuf));
continue;
}
strcat(sendbuf,clientinfo[tmp_i].name);
strcat(sendbuf,str3);
//因为这里是点对点,所以不用进入下面的部分,continue跳过
write(tmpid,sendbuf,sizeof(sendbuf));
continue;
}
}
else
{
strcat(sendbuf,clientinfo[tmp_i].name);
strcat(sendbuf," said: ");
strcat(sendbuf,recvbuf);
}
for(tmp_j = sockfd+1; tmp_j<=maxfd;tmp_j++)
{
//实现信息的广播
if(FD_ISSET(tmp_j,&allset))
{
write(tmp_j,sendbuf,strlen(sendbuf));
}
}
}
}
}
//清空sendbuf和recvbuf
memset(&sendbuf,0,BUFSIZE);
memset(&recvbuf,0,BUFSIZE);
}
return 0;
}
chatclient.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netdb.h>
#include<sys/time.h>
#include<sys/types.h>
#define PORT 1573
#define BUFSIZE 2048
int main(int argc, char *argv[])
{
int sockfd;
fd_set sockset; //套接字集合,用于判断是套接字还是I/O输入
struct sockaddr_in server;
struct sockaddr_in client;
int recvnum;
char sendbuf[BUFSIZE];
char recvbuf[BUFSIZE];
int length;
if(2>argc)
{
printf("please input ip!\n");
exit(1);
}
if(-1==(sockfd = socket(AF_INET,SOCK_STREAM,0)))
{
perror("create client socket error!\n");
exit(1);
}
memset(&server,0,sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(argv[1]);
server.sin_port = htons(PORT);
if(-1==connect(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr)))
{
perror("client connect error!\n");
exit(1);
}
memset(sendbuf,0,2048);
fprintf(stderr,"welcome to visit the chat server\n");
fprintf(stderr,"please input your name:");
fgets(sendbuf,256,stdin);
if(0>send(sockfd,sendbuf,strlen(sendbuf),0))
{
perror("sending data error!\n");
close(sockfd);
exit(1);
}
//初始化集合
FD_ZERO(&sockset);
FD_SET(sockfd,&sockset);
FD_SET(0,&sockset);
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
memset(sendbuf,0,sizeof(sendbuf));
select(sockfd+1,&sockset,NULL,NULL,NULL);
if(FD_ISSET(sockfd,&sockset))
{
//处理:如果是套接字被激活,表示服务器有信息传过来,进行接收处理
recvnum=read(sockfd,recvbuf,sizeof(recvbuf));
recvbuf[recvnum]='\0';
printf("%s\n",recvbuf);
printf("\n");
fflush(stdout);
}
if(FD_ISSET(0,&sockset))
{
//处理:如果是I/O被激活,表示客户有消息要发送出去,进行发送处理
fgets(sendbuf,sizeof(sendbuf),stdin);
length = strlen(sendbuf);
sendbuf[length] = '\0';
if(strcmp(sendbuf,"/help\n")==0)
{
//处理,输入"/help"表示想要得到帮助信息,帮助信息是存储在客户端的,不用向服务器发送信息
//跳过继续执行循环
printf("\n");
fprintf(stderr,"/help show the help message\n");
fprintf(stderr,"/send usage:/send user message send message to user\n");
fprintf(stderr,"/who show who is online\n");
fprintf(stderr,"/quit quit from server\n");
printf("\n");
continue;
}
write(sockfd,sendbuf,sizeof(sendbuf));
if(strcmp(sendbuf,"/quit\n")==0)
{
//处理,客户想要退出,关闭套接字,用户程序退出
printf("quiting from chat room!\n");
close(sockfd);
exit(1);
}
}
FD_ZERO(&sockset);
FD_SET(sockfd,&sockset);
FD_SET(0,&sockset);
}
close(sockfd);
return 0;
}
分享到:
相关推荐
利用select函数在linux环境下实现的一个聊天程序,满足要求: (1)用户默认出于广播模式,一个客户在其客户端发送消息,其他客户端用户全部都可以收到; (2)程序支持下列命令 /help:显示帮助信息 /quit:用户退出...
在Linux系统下构建一个聊天室是一项涉及网络编程和多线程技术的任务。下面将详细讲解这个过程中的关键知识点。 首先,我们需要了解TCP协议。TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输协议...
本项目涉及的核心技术是I/O复用,具体使用了Linux环境下的`select`函数来实现多客户端的聊天室功能。`select`是多路复用I/O模型的一种,它允许程序同时监控多个文件描述符(如socket),等待它们准备好进行读写操作...
标签明确了项目的技术栈:使用C语言编程,运行在Linux环境下,并且是一个聊天室应用。C语言是一种底层、高效的语言,非常适合编写操作系统级别的程序和网络通信软件。Linux作为开源操作系统,提供了丰富的API和系统...
总的来说,这个简单的聊天室项目是一个很好的学习资源,它演示了如何利用`select`函数进行多路复用,以及如何用C语言实现基本的TCP套接字编程。通过这个项目,开发者可以掌握网络编程的基础知识,包括套接字的创建、...
在Linux环境下创建一个简易聊天室是一项综合性的编程挑战,它涵盖了多个关键的技术领域。这个项目主要涉及以下几个核心知识点: 1. **Linux基本知识**:在Linux操作系统中开发应用程序,需要了解其文件系统、进程...
在这个名为"select多人聊天程序"的项目中,开发者使用C++语言在Linux环境下实现了这一功能,让多个用户能够通过socket进行实时通信。 首先,让我们深入了解`select`函数的工作原理。`select`函数接收三个参数:一个...
本文将深入探讨在`socket`编程中的一种多路复用技术——`select`模型,以及如何利用它来构建一个简单的网络聊天室,类似于QQ群组。 `socket`是操作系统提供的一种接口,用于在网络上的进程间进行通信。它支持TCP...
在Linux环境下,使用C语言开发一个基于TCP/IP的聊天室是一项具有挑战性的任务,它涉及到网络编程、套接字(socket)通信以及多线程等多个关键知识点。下面将详细阐述这些核心概念及其在构建聊天室中的应用。 首先,...
《基于Linux的网络聊天室设计与实现》是一个深入探讨如何在Linux操作系统环境下构建网络聊天室的项目。这个项目涵盖了多个IT领域的关键知识点,包括网络编程、多线程、并发处理、套接字通信以及Linux系统编程等。...
采用I/O复用技术select实现socket通信,采用多线程负责每个客户操作处理,完成Linux下的多客户聊天室! OS:Ubuntu 15.04 IDE:vim gcc make DB:Sqlite 3 Time:2015-12-09 ~ 2012-12-21 项目功能架构: 1. ...
以上就是构建基于C语言的Linux聊天室程序所涉及的关键知识点,这些技术和概念是理解并实现这样一个系统的基础。在实际开发过程中,还需要考虑错误处理、性能优化和用户体验等方面,以创建一个稳定、高效且易用的聊天...
在Linux环境下,构建一个基于TCP协议的多用户聊天室是一个典型的网络编程项目,它涉及到许多核心的计算机网络和操作系统知识。TCP(传输控制协议)是一种面向连接、可靠的传输协议,确保了数据的完整性和顺序性,...
本项目“Linux C Socket在QT上编写的简单聊天室”提供了一个实用的示例,它巧妙地结合了C语言、QT库以及Linux的Socket接口,展示了如何在QT环境下构建一个基本的多用户聊天系统。 首先,我们来了解一下Socket。...
在Linux环境下,构建一个简单的聊天室程序涉及到网络编程的核心概念,特别是使用socket接口进行通信。本文将深入探讨这个话题,包括Linux系统基础、网络编程基础知识以及如何通过C语言实现服务器端(server.c)和...
在本资源中,我们主要探讨的是Linux环境下的网络编程,特别是使用`select`函数实现一个异步通信的聊天程序。`select`是Linux系统中一个非常重要的I/O多路复用函数,它允许程序同时监控多个文件描述符,等待它们就绪...
本示例中,我们讨论的是一个基于"select"和"fd_set"机制的简单聊天室程序,这通常用于单线程服务器端通信。让我们深入了解一下这个话题。 首先,`select`函数是Unix和类Unix系统(包括Linux)中广泛使用的I/O多路...
### 一个简单聊天室的两种实现 (fcntl 和 select) #### 背景介绍 随着互联网技术的迅速发展,网络通信工具已经成为人们日常生活中不可或缺的一部分。其中,聊天室作为早期网络交流的重要形式之一,至今仍受到不少...
本项目“基于QT和select的多人网络聊天室”是一个实现客户端-服务器通信的经典案例,它利用了QT库的强大功能以及socket编程中的select模型来处理多个并发连接。以下是这个项目中涉及的重要知识点: 1. **QT库**:QT...