利用select实现一个聊天室程序

倖福魔咒の 提交于 2020-02-29 19:18:55

实验要求

  1. 用户默认处于广播模式,一个客户在其客户端发送的消息,其它客户端用户全部可以收到;
  2. 程序支持下列命令
    /help:显示帮助信息(思考:信息是放在客户端还是服务器端);
    /quit:用户退出聊天室,同时将退出信息广播给其他用户;
    /who:显示在线用户;
    /send 用户名 消息:向指定用户发送点到点消息
  3. 程序退出时清理所有的占用资源,包括内存资源、套接字等
  4. 支持最大连接数限制;
  5. 能够管理用户加入和退出。

实验环境

Red Hat 9

代码

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;        //Represents the socket the user is now accessing 
    char name[256];  
    int first;     //whether the user is visiting for the first time and is used to pass in the name
};  
  
int main(){  
    fd_set allset;   //All sockets that need to be scanned
    fd_set rset;     //Socket after 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;         //Define socket properties  
    int length;      
  
    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;   
    int tmpfd=-1;    
    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 listening socket is activated  
            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;  //Set first to 1 for the name of the first receive package
  
                    FD_SET(confd,&allset);  
                    if(confd > maxfd)  
                        maxfd = confd;  
                }  
                else{  
                    //If the connection socket is activated
                    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);  
                            }  

                            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!\n");  
                        }  
                          
                        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");  
			
						/*Split string*/
						int i;
						for(i=0; i<5; i++) {
							str1[i] = recvbuf[i];
						}
						str1[i] = '\0';

						for(i=6; i<strlen(recvbuf); i++){
							if(recvbuf[i]== ' ') { break; }
							str2[i-6] = recvbuf[i];			
						}
						str2[i-6] = '\0';
						strcat(str2, "\n");

						int j=0;
						for(; i<strlen(recvbuf); i++) {
							str3[j] = recvbuf[i];
							j++;
						}
						str3[j] = '\0';

                        if(strcmp(str1,"/send")==0)  
                        {  
                            tmpid = -1;
                            int j = 0;  
                            for(tmpfd = sockfd;tmpfd<=maxfd;tmpfd++)  
                            {  
                                //Queries the socket for the customer under the specified name
                                if(FD_ISSET(tmpfd,&allset))  
                                {  
                                    if(strcmp(str2,clientinfo[tmpfd].name)==0)  
                                        tmpid = tmpfd;  
                                }  
                            }  
                            if(tmpid==-1)  
                            {
                                strcat(sendbuf,"user isn't online!\n");  
                                write(tmp_i,sendbuf,sizeof(sendbuf));  
                                continue;  
                            }  
                            strcat(sendbuf,clientinfo[tmp_i].name); 
							strcat(sendbuf, "> "); 
                            strcat(sendbuf,str3);
							//strcat(sendbuf, "\n");  
                            write(tmpid,sendbuf,sizeof(sendbuf)); 
                            continue;  
                        }         
                    }  
                    else  
                    {  
                        strcat(sendbuf,clientinfo[tmp_i].name);  
                        strcat(sendbuf,"> ");  
                        strcat(sendbuf,recvbuf);  
                    }  
                    for(tmp_j = sockfd+1; tmp_j<=maxfd;tmp_j++)  
                    {  
                        //Implement the broadcast of information
                        if(FD_ISSET(tmp_j,&allset))  
                        {  
                            write(tmp_j,sendbuf,strlen(sendbuf));  
                        }  
                    }                
                }  
            }  
        }  
        //Clear sendbuf and 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; //A collection of sockets used to determine whether it is a socket or I/O input
    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);  
    }  
  
    //Initialize
    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))  
        {  
            //If the socket is activated, it means that the server has information to receive
            recvnum=read(sockfd,recvbuf,sizeof(recvbuf));  
            recvbuf[recvnum]='\0';  
            printf("%s\n",recvbuf);
	    //printf("\n");
            fflush(stdout);  
        }  
        if(FD_ISSET(0,&sockset))  
        {  
            //If I/O is activated, it means that the customer has a message to send out for sending processing
            fgets(sendbuf,sizeof(sendbuf),stdin); 
			printf("\n"); 
            length = strlen(sendbuf);  
            sendbuf[length] = '\0';  
              
            if(strcmp(sendbuf,"/help\n")==0)  
            {  
                //To get help information, help information is stored in the client, do not send information to the server 
                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)  
            {  
                //The client wants to exit, close the socket, and the user program exits
                printf("Quiting... Welcome next time!\n");
				printf("\n");  
                close(sockfd);  
                exit(1);  
            }  
        }  
        FD_ZERO(&sockset);  
        FD_SET(sockfd,&sockset);  
        FD_SET(0,&sockset);  
    }  
    close(sockfd);  
    return 0;  
}

运行结果

启动服务器端
在这里插入图片描述
启动三个客户端,g3发送了一个消息,显示在了所有的客户端界面上,从左上角到右下角依次为g1、g2、g3
在这里插入图片描述g3使用/send命令向g1发送了一句连续的字符串,只显示在g1界面;g2使用/send命令向g3发送了一句带空格的字符串,只成功显示在g3界面上,从上到下依次为g1、g2、g3
在这里插入图片描述
g1测试使用/help命令,g2使用/who命令,都只显示在自己的界面上
在这里插入图片描述
g1、g3、g2依次退出,从左到右依次为g1、g2、g3

在这里插入图片描述
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!