Day32

news/2024/5/18 13:16:59 标签: udp

基于UDP的网络聊天室

客户端

#include <myhead.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr, "line:%d %s\t%s", __LINE__, __FILE__, __func__);\
	perror(msg);\
}while(0)

#define IP "192.168.71.62"        //ifconfig出来的本机IP
#define PORT 6666                 //1024~49151,网络字节序

typedef struct
{
	char type;                    //L登录C群聊Q下载 
	char name[20];                //用户名
	char text[128];               //群聊内容
}msg_t;

int func_chat(int sfd, msg_t msg, struct sockaddr_in sin);
int func_recv(int sfd, msg_t msg, struct sockaddr_in sin);

void handler(int sig)
{
	//回收子进程
	wait(0);
	kill(getpid(),SIGINT);
}


int main(int argc, const char *argv[])
{
	//捕获17) SIGCHLD信号
	if(signal(SIGCHLD, handler) ==SIG_ERR)
	{
		ERR_MSG("signal");
		return -1;
	}


	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("create socket success sfd = %d __%d__\n", sfd, __LINE__);

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family      = AF_INET;        
	sin.sin_port        = htons(PORT);    
	sin.sin_addr.s_addr = inet_addr(IP);  

	//定义信息交流结构体
	msg_t msg;
	msg.type = 'L';
	printf("请输入用户名>>>>");
	fgets(msg.name, sizeof(msg.name), stdin);
	msg.name[strlen(msg.name)-1] = '\0';

	//发送登录信息
	if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto apply");
		return -1;
	}


		pid_t pid = fork();
		if(pid < 0)
		{
			ERR_MSG("fork");
			return -1;
		}
		else if(pid == 0)
		{
			//子进程
			//功能:群聊   下线
			func_chat(sfd, msg, sin);
		}
		else if(pid > 0)
		{


		//父进程
		//功能:接收聊天内容
			func_recv(sfd, msg, sin);
		}
			//关闭套接字
			close(sfd);

}

int func_chat(int sfd, msg_t msg, struct sockaddr_in sin)
{
	while(1)
	{
	//从终端获取数据
	fgets(msg.text, sizeof(msg.text), stdin);
	msg.text[strlen(msg.text)-1] = '\0';

	//退出群聊
	if(strcmp(msg.text, "quit") == 0)
	{
		msg.type = 'Q';
		//通知服务器,该用户下线
		if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
		{
			ERR_MSG("sendto quit");
			return -1;
		}
		exit(0);

	}

	//群聊
	msg.type = 'C';

	//向服务器发送数据
	if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto data");
		return -1;
	}
	}


	return 0;
}

int func_recv(int sfd, msg_t msg, struct sockaddr_in sin)
{
	while(1)
	{
	socklen_t addrlen = sizeof(sin);
	if(recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, &addrlen) < 0)
	{
		ERR_MSG("recvfrom");
		return -1;
	}
	printf("[%s]:%s \n",msg.name, msg.text);	

	}
	return 0;
}

服务器

#include <myhead.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr, "line:%d %s\t%s", __LINE__, __FILE__, __func__);\
	perror(msg);\
}while(0)

#define IP "192.168.71.62"        //ifconfig出来的本机IP
#define PORT 6666                 //1024~49151,网络字节序

//信息交流结构体
typedef struct
{
	char type;                    //L登录C群聊Q下载 
	char name[20];                //用户名
	char text[128];               //群聊内容
}msg_t;

//存储用户地址链表
typedef struct ADDR
{
	//数据域
	union
	{
		//头结点的数据域:链表长度
		int len;
		//其他节点的数据域:数据元素
		struct sockaddr_in 	cin;
	};
	//指针域:下一个节点的地址
	struct ADDR *next;
}*addrlist;

addrlist create(int flag);
int func_delete(addrlist list, struct sockaddr_in rcvaddr);
int func_send(addrlist list, int sfd, msg_t msg);
int func_log(addrlist list, struct sockaddr_in rcvaddr);

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("create socket success sfd = %d __%d__\n", sfd, __LINE__);

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family      = AF_INET;        
	sin.sin_port        = htons(PORT);    
	sin.sin_addr.s_addr = inet_addr(IP);  

	//绑定地址信息结构体
	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}

	//存储数据包是从哪里来的
	struct sockaddr_in rcvaddr;
    socklen_t addrlen_rcv = sizeof(rcvaddr);

	//定义信息交流结构体
	msg_t msg;
	
	//创建链表头结点
	addrlist list = create(1);
	//判断链表是否创建
	if(list == NULL)
	{
		printf("头插失败\n");
		return -1;
	}

	while(1)
	{
		//父进程
		//获取用户信息
		bzero(&msg, sizeof(msg));
		bzero(&rcvaddr, sizeof(rcvaddr));
		if(recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&rcvaddr, &addrlen_rcv) < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		
				pid_t pid = fork();
		if(pid < 0)
		{ 
			ERR_MSG("fork");
			return -1;
		}
		else if(pid == 0)
		{
			while(1)
			{
			//子进程
			//功能:发送系统信息
			
			fgets(msg.text, sizeof(msg.text), stdin);
			msg.text[strlen(msg.text)-1] = '\0';
			  strcpy(msg.name, "系统");
			  printf("%s\n",msg.text);
			func_send(list, sfd, msg);
			  printf("%s\n",msg.text);
			
			//子进程只发送服务器信息,必须要退出
			//exit(0);
			}
		}
		
		
		switch (msg.type)
		{
			case 'L':
				//转发登录信息给所有连接用户
			  sprintf(msg.text, "用户%s登录", msg.name);
			  printf("[系统]用户%s已登录\n", msg.name);
			  strcpy(msg.name, "系统");
				func_send(list, sfd, msg);
			  //录入登录用户信息
				func_log(list, rcvaddr);
				break;
		
			case 'Q':
				//删除下线用户信息
			  func_delete(list, rcvaddr);	
			  printf("%d\n",__LINE__);
			  if(list->len == 0)
			  {
				  printf("所有均用户下线\n");
				  return 0;
			  }
			  //转发用户下线信息
			  sprintf(msg.text, "用户%s下线", msg.name);
			  printf("[系统]用户%s已下线\n", msg.name);
			  strcpy(msg.name, "系统");
			  func_send(list, sfd, msg);
				break;
			
			case 'C':
				//发送聊天信息
			  func_send(list, sfd, msg);
				break;
			default:
				printf("rcvfrom error\n");
			break;
		}

		waitpid(-1, NULL, WNOHANG);
	}

	//关闭套接字
	close(sfd);	
	return 0;
}

int func_log(addrlist list, struct sockaddr_in rcvaddr)
{
		//将用户信息插入链表中
		//头插 创建一个新节点s
		addrlist s = create(0);
		if(s == NULL)
		{
			return -1;
		}
		//新节点s创建成功 s的数据域赋值
		s->cin.sin_port        = rcvaddr.sin_port;    
		s->cin.sin_addr.s_addr = rcvaddr.sin_addr.s_addr;  
	
		//s的指针域
		s->next = list->next;
		list->next = s;
		list->len++;

		return 0;
}

int func_send(addrlist list, int sfd, msg_t msg)
{
	addrlist temp = list;
	while(temp->next != NULL)
	{
		  temp = temp->next;
			if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(temp->cin), sizeof(temp->cin)) < 0)
			{
				ERR_MSG("sendto");
				return -1;
			}
	}
	
	return 0;
}

int func_delete(addrlist list, struct sockaddr_in rcvaddr)
{
	addrlist temp = list;
	while(temp->next != NULL)
	{
		if(rcvaddr.sin_addr.s_addr == temp->next->cin.sin_addr.s_addr)
		{
			addrlist del = temp->next;
			temp->next = del->next;
			free(del);
			list->len--;
		}
		temp = temp->next;		
	}
	
	return 0;
}

addrlist create(int flag)
{
	//创建头结点list
	addrlist list = (addrlist)malloc(sizeof(struct ADDR));
	if(list == NULL)
	{
		return NULL;
	}
	//对头结点的list的数据域初始化
	if(flag == 1)
		list->len = 0;
	else if(flag == 0)
	{
		list->cin.sin_family      = AF_INET;        
		list->cin.sin_port        = htons(PORT);    
		list->cin.sin_addr.s_addr = inet_addr(IP);  
	}
	//对头结点的指针域初始化
	list->next = NULL;
	
	return list;
}


http://www.niftyadmin.cn/n/423697.html

相关文章

ChatGPT 会取代程序员吗?

ChatGPT 是由 OpenAI 于 2022 年 11 月 30 日推出的智能聊天机器人。由于技术表现非常优秀&#xff0c;一出道就火爆全球。它不仅让谷歌、苹果等 IT 巨头睡不着觉&#xff0c;还成功吸引了微软 100 亿美金的技术投资。 第一波吃到螃蟹的道友开始用它生成代码&#xff0c;玩得不…

DAY 72 redis高可用的主从复制、哨兵、cluster集群

Redis 高可用 什么是高可用 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务(99.9%、99.99%、99.999%等等)。 但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xff0c;除了保证提供正常服…

GIS软件中网络分析的5种应用

什么是网络分析&#xff1f;几乎每个人都需要一个网络分析的类型在他们的生活中。 例如&#xff0c;去海滩的最短路线是什么&#xff1f;应该在哪里建一所医院来最好地服务一个社区&#xff1f;如何优化运输车队&#xff1f; 以下是5种最常见的网络分析类型&#xff1a;点对点…

JSP概述

在servlet当中编写HTML/CSS/JS等前端代码&#xff0c;存在的问题: java程序中编写前端代码&#xff0c;编写难度大&#xff0c;麻烦 程序耦合度高&#xff0c;代码不美观&#xff0c;不宜于维护 修改一个小小的前端代码&#xff0c;必须重新编译class文件&#xff0c;打一个war…

老板谈上4休3工作制1个月后效果:不建议新公司模仿

大家好&#xff01;我是老洪&#xff01; 刚看到一则关于上4休3工作制的资讯&#xff0c;聊两句。 媒体是这样报道的。 一位长沙公司的老板在实施上4休3工作制一个月后表示&#xff0c;不建议新公司模仿。 他指出&#xff0c;个别员工的自律问题在上四休三后暴露出来&#xff0…

工厂模式~

核心本质 ① 实例化对象不使用new&#xff0c;用工厂方法代替 ② 将选择实现类&#xff0c;创建对象统一管理和控制&#xff0c;从而将调用者跟我们的实现类解耦 简单工厂 public interface Car {void name(); }public class Tesla implements Car{Overridepublic void name()…

什么是Vue的JSX语法?如何使用JSX语法?

什么是Vue的JSX语法&#xff1f;如何使用JSX语法&#xff1f; 在Vue中&#xff0c;我们通常使用模板语法来编写组件的模板。但是&#xff0c;有些开发者更喜欢使用类似于React的JSX语法来编写组件。Vue也支持使用JSX语法来编写组件&#xff0c;本文将介绍什么是Vue的JSX语法以…

Spring配置动态数据库

前言 本文主要介绍使用spring boot 配置多个数据库&#xff0c;即动态数据库 开始搭建 首先创建一个SpringWeb项目——dynamicdb(spring-boot2.5.7) 然后引入相关依赖lombok、swagger2、mybatis-plus&#xff0c;如下&#xff1a; <?xml version"1.0" encoding&q…