实验要求
- 用户默认处于广播模式,一个客户在其客户端发送的消息,其它客户端用户全部可以收到;
- 程序支持下列命令
/help:显示帮助信息(思考:信息是放在客户端还是服务器端);
/quit:用户退出聊天室,同时将退出信息广播给其他用户;
/who:显示在线用户;
/send 用户名 消息:向指定用户发送点到点消息 - 程序退出时清理所有的占用资源,包括内存资源、套接字等
- 支持最大连接数限制;
- 能够管理用户加入和退出。
实验环境
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
来源:CSDN
作者:冷枭子
链接:https://blog.csdn.net/qq_42316621/article/details/104574249