`
minglaihan
  • 浏览: 16586 次
  • 性别: Icon_minigender_1
  • 来自: 天津
文章分类
社区版块
存档分类
最新评论

利用select函数实现在Linux环境下实现一个聊天室程序

 
阅读更多
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;
}


  


  
分享到:
评论

相关推荐

    linux下select函数实现的聊天程序

    利用select函数在linux环境下实现的一个聊天程序,满足要求: (1)用户默认出于广播模式,一个客户在其客户端发送消息,其他客户端用户全部都可以收到; (2)程序支持下列命令 /help:显示帮助信息 /quit:用户退出...

    linux系统下实现聊天室

    在Linux系统下构建一个聊天室是一项涉及网络编程和多线程技术的任务。下面将详细讲解这个过程中的关键知识点。 首先,我们需要了解TCP协议。TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输协议...

    采用I/O复用技术select实现socket通信,完成Linux下的多客户聊天室!

    本项目涉及的核心技术是I/O复用,具体使用了Linux环境下的`select`函数来实现多客户端的聊天室功能。`select`是多路复用I/O模型的一种,它允许程序同时监控多个文件描述符(如socket),等待它们准备好进行读写操作...

    linux网络聊天室(select服务器)

    标签明确了项目的技术栈:使用C语言编程,运行在Linux环境下,并且是一个聊天室应用。C语言是一种底层、高效的语言,非常适合编写操作系统级别的程序和网络通信软件。Linux作为开源操作系统,提供了丰富的API和系统...

    select函数写的简单聊天室

    总的来说,这个简单的聊天室项目是一个很好的学习资源,它演示了如何利用`select`函数进行多路复用,以及如何用C语言实现基本的TCP套接字编程。通过这个项目,开发者可以掌握网络编程的基础知识,包括套接字的创建、...

    linux下的简易聊天室

    在Linux环境下创建一个简易聊天室是一项综合性的编程挑战,它涵盖了多个关键的技术领域。这个项目主要涉及以下几个核心知识点: 1. **Linux基本知识**:在Linux操作系统中开发应用程序,需要了解其文件系统、进程...

    select多人聊天程序

    在这个名为"select多人聊天程序"的项目中,开发者使用C++语言在Linux环境下实现了这一功能,让多个用户能够通过socket进行实时通信。 首先,让我们深入了解`select`函数的工作原理。`select`函数接收三个参数:一个...

    网络聊天室(socket中的select模型)

    本文将深入探讨在`socket`编程中的一种多路复用技术——`select`模型,以及如何利用它来构建一个简单的网络聊天室,类似于QQ群组。 `socket`是操作系统提供的一种接口,用于在网络上的进程间进行通信。它支持TCP...

    linux下c实现的聊天室

    在Linux环境下,使用C语言开发一个基于TCP/IP的聊天室是一项具有挑战性的任务,它涉及到网络编程、套接字(socket)通信以及多线程等多个关键知识点。下面将详细阐述这些核心概念及其在构建聊天室中的应用。 首先,...

    基于Linux的网络聊天室的设计与实现

    《基于Linux的网络聊天室设计与实现》是一个深入探讨如何在Linux操作系统环境下构建网络聊天室的项目。这个项目涵盖了多个IT领域的关键知识点,包括网络编程、多线程、并发处理、套接字通信以及Linux系统编程等。...

    Linux聊天室 -- select && 多线程实现

    采用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 系统下的聊天室程序【100012562】

    以上就是构建基于C语言的Linux聊天室程序所涉及的关键知识点,这些技术和概念是理解并实现这样一个系统的基础。在实际开发过程中,还需要考虑错误处理、性能优化和用户体验等方面,以创建一个稳定、高效且易用的聊天...

    linux下基于TCP的多用户聊天室含文档.z

    在Linux环境下,构建一个基于TCP协议的多用户聊天室是一个典型的网络编程项目,它涉及到许多核心的计算机网络和操作系统知识。TCP(传输控制协议)是一种面向连接、可靠的传输协议,确保了数据的完整性和顺序性,...

    linux C socket 在QT上编写的简单聊天室

    本项目“Linux C Socket在QT上编写的简单聊天室”提供了一个实用的示例,它巧妙地结合了C语言、QT库以及Linux的Socket接口,展示了如何在QT环境下构建一个基本的多用户聊天系统。 首先,我们来了解一下Socket。...

    Linux下的简单聊天室

    在Linux环境下,构建一个简单的聊天室程序涉及到网络编程的核心概念,特别是使用socket接口进行通信。本文将深入探讨这个话题,包括Linux系统基础、网络编程基础知识以及如何通过C语言实现服务器端(server.c)和...

    Linux_chatroom_select.rar_Linux proc虚拟系统_Linux 聊天_linux c selec

    在本资源中,我们主要探讨的是Linux环境下的网络编程,特别是使用`select`函数实现一个异步通信的聊天程序。`select`是Linux系统中一个非常重要的I/O多路复用函数,它允许程序同时监控多个文件描述符,等待它们就绪...

    简单的聊天室程序

    本示例中,我们讨论的是一个基于"select"和"fd_set"机制的简单聊天室程序,这通常用于单线程服务器端通信。让我们深入了解一下这个话题。 首先,`select`函数是Unix和类Unix系统(包括Linux)中广泛使用的I/O多路...

    一个简单聊天室的两种实现 (fcntl 和 select)

    ### 一个简单聊天室的两种实现 (fcntl 和 select) #### 背景介绍 随着互联网技术的迅速发展,网络通信工具已经成为人们日常生活中不可或缺的一部分。其中,聊天室作为早期网络交流的重要形式之一,至今仍受到不少...

    基于QT和select的多人网络聊天室 Qt(客户端) Socket(服务端)

    本项目“基于QT和select的多人网络聊天室”是一个实现客户端-服务器通信的经典案例,它利用了QT库的强大功能以及socket编程中的select模型来处理多个并发连接。以下是这个项目中涉及的重要知识点: 1. **QT库**:QT...

Global site tag (gtag.js) - Google Analytics