编写一个TCP吐槽聊天室,接收来自各方TCP客户端的吐槽信息,并将信息直接输出到屏幕上

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

要求:
1.能随时接收任何客户端的连接和信息。
2.建立一定的防刷屏机制,使得客户端的吐槽不能太快。
3.显示当前的连接总数。
4.如果使用多线程或者多进程,注意防范僵尸。
升级版1
升级版2
运行结果:
在这里插入图片描述
在这里插入图片描述

具体代码:

/*
	服务器部分:
*/
#include <stdio.h>	
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "list.h"

int tcpsock;
int newsock;
struct node *list;

void finish()
{
    
    
	struct node *p = list->next;
	while(p != list)													//循环读取每个节点
	{
    
    	
		write(p->data,"quit",4);										//给q发送退出信息	
		p = p->next;													//下一节点
	}
	close(tcpsock);
	close(newsock);
	exit(0);
}
struct arg																//定义结构体存储文件描述符
{
    
    
	struct node *list1;													//存放所有服务器的链表
	struct node *p1;													//存放新连接的服务器节点
};

void *transfer(void *arg)
{
    
    
	
	struct arg *arg1 = arg;
	struct node *head = arg1->list1;
	struct node *q = arg1->p1;
	
	struct node *p ;
	char buf[100] = {
    
    0};												//存放客户端发送的消息
	char newbuf[100] = {
    
    0};												//存放向其他客户端发送的消息									
	while(1)
	{
    
    	
		p = head->next;
		
		bzero(&buf,100);
		if(read(q->data,buf,100))										//q客户没有断开
		{
    
    
			while(p!=head)												//循环读取每个节点
			{
    
    
				if(p->data != q->data )									//判断是否为p节点
				{
    
    
					//printf("%d receive: %s\n",p->data,buf);				//输出q的文件描述符
					bzero(&newbuf,100);
					sprintf(newbuf,"%d:  %s",q->data,buf);
					write(p->data,newbuf,strlen(newbuf));				//给q发送p的信息
				}
				p = p->next;											//下一节点
			}
		}
		
		else
		{
    
    
			delnode(q, head);											//客户端断开,删除对应节点
		}
	}
}

int main()
{
    
    
	int ret;
	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(10086); 									//绑定服务器自己的端口号

	struct sockaddr_in clientaddr;
	bzero(&clientaddr,sizeof(clientaddr));
	int addrsize=sizeof(clientaddr);
	
	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=listen(tcpsock,8);
	if(ret==-1)
	{
    
    
		perror("监听失败!\n");
		return -1;
	}
	
	list = list_init();
	signal(SIGINT,finish);
	int i = 0;
	while(1)
	{
    
    
		newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);//接收客户端的连接请求
		if(newsock==-1)
		{
    
    
			perror("接收客户端的连接请求失败!\n");
			return -1;
		}
		printf("ip == %s\n",inet_ntoa(clientaddr.sin_addr));
		printf("端口号 == %d ",ntohs(clientaddr.sin_port));
		
		struct node *new = malloc(sizeof(struct node));					//创建新节点
					
		new = newnode(newsock,											//新节点存放新的文件描述符
						inet_ntoa(clientaddr.sin_addr),					//存放新连接的服务器的ip地址
						ntohs(clientaddr.sin_port));						
						
		addnode(new,list);												//插入新节点
		printf("新的文件描述符:%d\n",newsock);
		
		struct arg *arg1 = malloc(sizeof(struct arg));					//定义结构体存放参数,传给新线程任务函数
		arg1->list1 = list;												
		arg1->p1 = new;

		pthread_t id[i];
		pthread_create(&id[i],NULL,&transfer,arg1);						//每次接受客户端连接都创建一个线程
		i++;
	}
}
/*
	客户端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;
}
#ifndef _LIST_H
#define _LIST_H

//设置节点
struct node
{
    
    
	char *ip;								//节点存放IP地址
	int port;								//节点存放端口号
	int data;
	struct node *next;
	struct node *prev;
};

//初始化链表
struct node *list_init()
{
    
    
	struct node *head = malloc(sizeof(struct node));
	head->next = head;
	head->prev = head;
	return head;
}

//创建新节点
struct node *newnode(int newdata,char *IP, int port)
{
    
    
	struct node *new = malloc(sizeof(struct node));
	new->ip = IP;
	new->port = port;
	new->data = newdata;
	new->next = NULL;
	new->prev = NULL;
}

//加入新节点
int addnode(struct node *new,struct node *list)
{
    
    
	int sum = 1;							//初始值为1
	struct node *p = list;
	while(p->next != list)
	{
    
    
		p = p->next;
		sum ++;								//每个节点代表每个客户端
	}
	new->prev = list->prev;
	new->next = list;
	list->prev = new;
	new->prev->next = new;
	printf("总链接数:%d\n",sum);			//输出当前总链接数
}

//删除节点
int delnode(struct node *q, struct node *head)
{
    
    
	struct node *p = head;
	struct node *p1 = NULL;
	while(p->next != head)
	{
    
    
		if(p == q)
		{
    
    
			p1 = p->prev;
			p->prev->next = p->next;
			p->next->prev = p->prev;
			p->next = NULL;
			p->prev = NULL;
			free(p);
			p = p1;
		}
		p = p->next;
	}
	
	if(p->next == head && p == q)
	{
    
    
		p->prev->next = head;
		head->prev = p->prev;
		p->next = NULL;
		p->prev = NULL;
	}
}
#endif

猜你喜欢

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