编写一个TCP通信,接收来自各方TCP客户端的信息,将信息直接输出到屏幕上,并将信息群发给所有已经连上的客户端。

更多资料请点击:我的目录
本篇仅用于记录自己所学知识及应用,代码仍可优化,仅供参考,如果发现有错误的地方,尽管留言于我,谢谢。

编写一个TCP吐槽聊天室,接收来自各方TCP客户端的吐槽信息,将信息直接输出到屏幕上,并将信息群发给所有已经连上的客户端。
要求:
1.客户端可以根据需要,发送群发消息,或者指定某个人发送私聊消息。
2.客户端可以屏蔽别人的群发消息。
3.服务端仅展示群发消息,不展示私聊消息。
4.当客户端指定私聊的对端不存在或者已经下线时,服务端给发送者必要的提示。

运行结果:
在这里插入图片描述

/*
	服务器部分
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <arpa/inet.h>
#include <netinet/in.h>


int clients = 0; 													//记录当前的已连接用户数量
int csum = 0;
int now = 0;

struct user															// 用户节点
{
    
    
    int connfd;														//套接字
    struct sockaddr_in addr;										//IP地址
};
	
struct user users[50];												//存放客户端的信息
struct user senus[50];												//存放需要群发信息的客户端(屏蔽别人的群发消息)

void finish()
{
    
    
	for(int k=0; k<clients; k++)
	{
    
    
		write(users[k].connfd,"quit",4);							//当服务器中断时,向服务器发送quit
		close(users[k].connfd);
	}
	exit(0);
}

int main ()
{
    
    
	int tcpskck=socket(AF_INET, SOCK_STREAM, 0);					//创建套接字
	
	int on = 1;
    setsockopt(tcpskck, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));	//设置端口为断开后可以立即重复使用
	
	struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    bzero(&addr, len);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY); 						//自动获取本机IP
    addr.sin_port   = htons(10086); 								//端口号PORT

	if(bind(tcpskck, (struct sockaddr *)&addr, len) == -1)			//绑定地址
    {
    
    
        perror("绑定地址失败");
        exit(0);
    }
	
	listen (tcpskck,3);												//进入监听状态
																	//准备3个套接字集合
    fd_set rset; 													//专门用来监控读就绪状态
    fd_set wset; 													//专门用来监控写就绪状态
    fd_set eset; 													//专门用来监控异常就绪状态
	 
    int maxfd = 0;													//坐等各方的连接,并顺便获取对方的地址
    char buf[100] = {
    
    0};
	char *to_ip;													//存放客户端发送给指定客户端的ip地址
	int to_port;													//存放客户端发送给指定客户端的端口号
	char to_buf[100] = {
    
    0};											//存放客户端发送的指定消息
	
	while(1)
    {
    
    
		signal(SIGINT,finish);
		
        FD_ZERO(&rset);												//套接字集合清零
        FD_ZERO(&wset);
        FD_ZERO(&eset);
																	//将监听套接字fd统统放进对应的集合中
        FD_SET(tcpskck, &rset); 									//监控fd的读就绪状态
        maxfd = tcpskck;

        for(int k=0; k<clients; k++)								//将所有已连接套接字connfd统统放进对应的集合中
        {
    
    
            FD_SET(users[k].connfd, &rset); 						//监控connfd的读就绪状态
            FD_SET(users[k].connfd, &eset); 						//监控connfd的异常就绪状态
			maxfd = maxfd> users[k].connfd ? maxfd : users[k].connfd;
        }

        // 多路监控:
        // select随即进入睡眠,等待直到三个集合中出现一个或多个套接字就绪
        // 其余未就绪的套接字,将会被select从集合中抹除
        // select的返回值,是就绪的套接字的个数
        select(maxfd+1, &rset, &wset, &eset, NULL);					//NULL为无限等待

        // 逐个判断套接字,看看哪个存留在集合中
        // 存留的套接字就是就绪的,被抹除的套接字就是未就绪的
        if(FD_ISSET(tcpskck, &rset)) 								//有新连接请求
        {
    
    
            bzero(&users[clients], sizeof(struct user));
            users[clients].connfd = accept(tcpskck, (struct sockaddr *)&users[clients].addr, &len);
            senus[clients] = users[clients];						//同步senus与users
			if(users[clients].connfd > 0)
            {
    
    
                printf("【%s:%u】连接成功!\n", inet_ntoa(users[clients].addr.sin_addr),
                                        ntohs(users[clients].addr.sin_port));

                printf("当前已连接的客户端数目:【%d】\n", ++clients);
				csum = clients;										//需要发消息的总数,与clients同步
            }
        }

        // 逐个判断已连接套接字connfd,看看哪个有数据
        for(int k=0; k<clients; k++)
        {
    
    
            if(FD_ISSET(users[k].connfd, &rset)) 					//该客户端发来消息
            {
    
    
				now = users[k].connfd;								//获取自己的套接字
                bzero(buf, 100);
                if(read(users[k].connfd, buf, 100) == 0)			//判断客户端是否断开,读取消息
                {
    
    
                    printf("【%s:%u】已断开连接,再见!\n", inet_ntoa(users[k].addr.sin_addr),
                                 ntohs(users[k].addr.sin_port));
								 
                    close(users[k].connfd);
					printf("当前已连接的客户端数目:【%d】\n", --clients);
					csum = clients;									//需要发消息的总数,与clients同步
					for(int j = k; j < clients; j++)				//客户端断开后,将后面所有客户端的套接字向前移
					{
    
    
						users[j].connfd = users[j+1].connfd;
						senus[j].connfd = senus[j+1].connfd;
					}	
					continue;
                }
								
				int flag = 0;
				{
    
    
					if(strstr(buf,"-") != NULL)						//指定信息格式: IP-PORT-message
					{
    
    
						flag = 1;
						bzero(to_buf,100);
						to_ip = (strtok(buf,"-"));					//获取发送给指定客户端的ip地址
						to_port = atoi(strtok(NULL,"-"));			//获取发送给指定客户端的端口号
						sprintf(to_buf,"%s",(strtok(NULL,"-")));	//获取指定的信息
						
						if(strstr(to_buf,"no") != NULL)				//屏蔽群发的消息
						{
    
    
							for(int i = k; i < clients-1; i++)
							{
    
    
								senus[i] = users[i+1];
							}
							csum--;									//需要发消息的总数--
							continue;
						}
						
						for(int i=0; i<clients; i++)
						{
    
    		
							//排除把消息发回给自己					
							if(strstr((inet_ntoa(users[i].addr.sin_addr)),to_ip)!=NULL && to_port == ntohs(users[i].addr.sin_port))						
							{
    
    
								write(users[i].connfd,to_buf,strlen(to_buf));
							}
						}	
					}
	
					if(flag == 0)
					{
    
    
						for(int i=0; i<csum; i++)
						{
    
    
							if(senus[i].connfd != now)				//排除把消息发回给自己
							{
    
    
								write(senus[i].connfd,buf,strlen(buf));
							}
						}	
						printf("【%s:%u】:%s\n", inet_ntoa(users[k].addr.sin_addr),
										  ntohs(users[k].addr.sin_port), buf);
					}
				}
            }

            if(FD_ISSET(users[k].connfd, &eset)) 					// 该客户端发来紧急数据
            {
    
    
                bzero(buf, 100);
                if(recv(users[k].connfd, buf, 100, MSG_OOB) == 0)
                {
    
    
                    printf("【%s:%u】已断开连接,再见!\n", inet_ntoa(users[k].addr.sin_addr),
                                 ntohs(users[k].addr.sin_port));
								 
                    close(users[k].connfd);
					printf("当前已连接的客户端数目:【%d】\n", --clients);
					csum = clients;
					for(int j = k; j < clients; j++)				//客户端断开后,将后面所有客户端的套接字向前移
					{
    
    
						users[j].connfd = users[j+1].connfd;
						senus[j].connfd = senus[j+1].connfd;
					}
					continue;
                }
                printf("【%s:%u】:(紧急数据)%s\n", inet_ntoa(users[k].addr.sin_addr),
                                      ntohs(users[k].addr.sin_port), buf);
            }
        }
    }
    return 0;
}
/*
	客户端1部分:
*/
#include <stdio.h>	
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>

time_t now ;
struct tm *tm_now ;															//定义当前时间结构体
int tcpsock;	
int m = 61, s = 61;															//定义分、秒初始值为61
pthread_t id;
	
void *fun()									
{
    
    
	char recv[100];
	while(1)
	{
    
    
		bzero(recv,100);
		if(read(tcpsock,recv,100))											//接收服务器发送的消息
		{
    
    
			if(strstr(recv,"quit") != NULL)									//判断时候收到“quit”(服务器中断)
			{
    
    	
				printf("服务器出错!\n");
				close(tcpsock);
				exit(0);
			}
			printf("receive %s\n",recv);
		}
	}
}
	
void finish()
{
    
    
	char fbuf[10] = {
    
    0};
	sprintf(fbuf,"%d gone!\n",tcpsock);										//向其他所有客户端发送退出信息
	write(tcpsock,fbuf,strlen(fbuf));										//当客户端中断时,向服务器发送gone
	close(tcpsock);
	pthread_cancel(id);
	exit(0);
}
	
int main()
{
    
    
	int ret;
	char buf[100];
	
	struct sockaddr_in bindaddr;											//定义ipv4地址结构体变量
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=inet_addr("192.168.1.157"); 	 				//绑定客户端自己的ip地址
	bindaddr.sin_port=htons(10000); 										//绑定客户端自己的端口号
	
	struct sockaddr_in serveraddr;											//定义结构体变量存放对方(服务器)的ip和端口号
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.1.157");  				//服务器的ip地址
	serveraddr.sin_port=htons(10086); 										//服务器的端口号
	
	tcpsock=socket(AF_INET,SOCK_STREAM,0);									//创建tcp套接字
	if(tcpsock==-1)
	{
    
    
		perror("创建tcp套接字!\n");
		return -1;
	}
	
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));				//设置端口释放后立即就可以被再次使用
	
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));		//绑定ip和端口号
	if(ret==-1)
	{
    
    
		perror("绑定失败!\n");
		return -1;
	}
	
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));	//连接服务器
	if(ret==-1)
	{
    
    
		perror("连接服务器!\n");
		return -1;
	}
	
	signal(SIGINT,finish);
	
	pthread_create(&id,NULL,&fun,NULL);
	
	printf("请输入要发送给服务器的信息!\n");								//发送信息给服务器
	while(1)
	{
    
    
		bzero(buf,100);
		scanf("%s",buf);
		if(strstr(buf,"quit")!=NULL)										//当客户端输入quit退出时,调用finish()函数
		{
    
    
			finish();														//向服务器发送quit
		}
		{
    
    
			time(&now) ;													//获取当前时间
			tm_now = localtime(&now);
			if(m == tm_now->tm_min && tm_now->tm_sec - s < 1)				//如果分相等,秒相差1s
			{
    
    
				printf("你说话太快了,请歇息一下再来!\n");
			}
			else
			{
    
    
				write(tcpsock,buf,strlen(buf));								//发送消息
				time(&now) ;												//获取本次消息发送的时间
				tm_now = localtime(&now);
				m = tm_now->tm_min;
				s = tm_now->tm_sec ;
			}	
		}	
	}
	close(tcpsock);
	return 0;
}
/*
	客户端2部分:
*/
#include <stdio.h>	
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>

time_t now ;
struct tm *tm_now ;															//定义当前时间结构体
int tcpsock;	
int m = 61, s = 61;															//定义分、秒初始值为61
pthread_t id;
	
void *fun()									
{
    
    
	char recv[100];
	while(1)
	{
    
    
		bzero(recv,100);
		if(read(tcpsock,recv,100))											//接收服务器发送的消息
		{
    
    
			if(strstr(recv,"quit") != NULL)									//判断时候收到“quit”(服务器中断)
			{
    
    	
				printf("服务器出错!\n");
				close(tcpsock);
				exit(0);
			}
			printf("receive %s\n",recv);
		}
	}
}
	
void finish()
{
    
    
	char fbuf[10] = {
    
    0};
	sprintf(fbuf,"%d gone!\n",tcpsock);										//向其他所有客户端发送退出信息
	write(tcpsock,fbuf,strlen(fbuf));										//当客户端中断时,向服务器发送gone
	close(tcpsock);
	pthread_cancel(id);
	exit(0);
}
	
int main()
{
    
    
	int ret;
	char buf[100];
	
	struct sockaddr_in bindaddr;											//定义ipv4地址结构体变量
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=inet_addr("192.168.1.157"); 	 				//绑定客户端自己的ip地址
	bindaddr.sin_port=htons(20000); 										//绑定客户端自己的端口号
	
	struct sockaddr_in serveraddr;											//定义结构体变量存放对方(服务器)的ip和端口号
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.1.157");  				//服务器的ip地址
	serveraddr.sin_port=htons(10086); 										//服务器的端口号
	
	tcpsock=socket(AF_INET,SOCK_STREAM,0);									//创建tcp套接字
	if(tcpsock==-1)
	{
    
    
		perror("创建tcp套接字!\n");
		return -1;
	}
	
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));				//设置端口释放后立即就可以被再次使用
	
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));		//绑定ip和端口号
	if(ret==-1)
	{
    
    
		perror("绑定失败!\n");
		return -1;
	}
	
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));	//连接服务器
	if(ret==-1)
	{
    
    
		perror("连接服务器!\n");
		return -1;
	}
	
	signal(SIGINT,finish);
	
	pthread_create(&id,NULL,&fun,NULL);
	
	printf("请输入要发送给服务器的信息!\n");								//发送信息给服务器
	while(1)
	{
    
    
		bzero(buf,100);
		scanf("%s",buf);
		if(strstr(buf,"quit")!=NULL)										//当客户端输入quit退出时,调用finish()函数
		{
    
    
			finish();														//向服务器发送quit
		}
		{
    
    
			time(&now) ;													//获取当前时间
			tm_now = localtime(&now);
			if(m == tm_now->tm_min && tm_now->tm_sec - s < 1)				//如果分相等,秒相差1s
			{
    
    
				printf("你说话太快了,请歇息一下再来!\n");
			}
			else
			{
    
    
				write(tcpsock,buf,strlen(buf));								//发送消息
				time(&now) ;												//获取本次消息发送的时间
				tm_now = localtime(&now);
				m = tm_now->tm_min;
				s = tm_now->tm_sec ;
			}	
		}	
	}
	close(tcpsock);
	return 0;
}
/*
	客户端3部分:
*/
#include <stdio.h>	
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>

time_t now ;
struct tm *tm_now ;															//定义当前时间结构体
int tcpsock;	
int m = 61, s = 61;															//定义分、秒初始值为61
pthread_t id;
	
void *fun()									
{
    
    
	char recv[100];
	while(1)
	{
    
    
		bzero(recv,100);
		if(read(tcpsock,recv,100))											//接收服务器发送的消息
		{
    
    
			if(strstr(recv,"quit") != NULL)									//判断时候收到“quit”(服务器中断)
			{
    
    	
				printf("服务器出错!\n");
				close(tcpsock);
				exit(0);
			}
			printf("receive %s\n",recv);
		}
	}
}
	
void finish()
{
    
    
	char fbuf[10] = {
    
    0};
	sprintf(fbuf,"%d gone!\n",tcpsock);										//向其他所有客户端发送退出信息
	write(tcpsock,fbuf,strlen(fbuf));										//当客户端中断时,向服务器发送gone
	close(tcpsock);
	pthread_cancel(id);
	exit(0);
}
	
int main()
{
    
    
	int ret;
	char buf[100];
	
	struct sockaddr_in bindaddr;											//定义ipv4地址结构体变量
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET;
	bindaddr.sin_addr.s_addr=inet_addr("192.168.1.157"); 	 				//绑定客户端自己的ip地址
	bindaddr.sin_port=htons(30000); 										//绑定客户端自己的端口号
	
	struct sockaddr_in serveraddr;											//定义结构体变量存放对方(服务器)的ip和端口号
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr("192.168.1.157");  				//服务器的ip地址
	serveraddr.sin_port=htons(10086); 										//服务器的端口号
	
	tcpsock=socket(AF_INET,SOCK_STREAM,0);									//创建tcp套接字
	if(tcpsock==-1)
	{
    
    
		perror("创建tcp套接字!\n");
		return -1;
	}
	
	int on=1;
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));				//设置端口释放后立即就可以被再次使用
	
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));		//绑定ip和端口号
	if(ret==-1)
	{
    
    
		perror("绑定失败!\n");
		return -1;
	}
	
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));	//连接服务器
	if(ret==-1)
	{
    
    
		perror("连接服务器!\n");
		return -1;
	}
	
	signal(SIGINT,finish);
	
	pthread_create(&id,NULL,&fun,NULL);
	
	printf("请输入要发送给服务器的信息!\n");								//发送信息给服务器
	while(1)
	{
    
    
		bzero(buf,100);
		scanf("%s",buf);
		if(strstr(buf,"quit")!=NULL)										//当客户端输入quit退出时,调用finish()函数
		{
    
    
			finish();														//向服务器发送quit
		}
		{
    
    
			time(&now) ;													//获取当前时间
			tm_now = localtime(&now);
			if(m == tm_now->tm_min && tm_now->tm_sec - s < 1)				//如果分相等,秒相差1s
			{
    
    
				printf("你说话太快了,请歇息一下再来!\n");
			}
			else
			{
    
    
				write(tcpsock,buf,strlen(buf));								//发送消息
				time(&now) ;												//获取本次消息发送的时间
				tm_now = localtime(&now);
				m = tm_now->tm_min;
				s = tm_now->tm_sec ;
			}	
		}	
	}
	close(tcpsock);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43793181/article/details/108501871