简易网络聊天室:2024/3/7

news2024/12/25 9:34:10

思维导图

基于UDP的简易网络聊天室

服务器:

#include <myhead.h>

#define SER_PORT 8888

struct msgTyp  //存储消息的结构体
{
	char type; 		  //消息类型
	char name[30];    //用户姓名
	char text[1024];  //消息正文
};

//创建链表存储客户端信息
typedef struct node
{
	//数据域
	struct sockaddr_in cin;
	//指针域,存储下一个节点的地址
	struct node *next;
}*LinkList, Node;

//创建链表节点
LinkList Create()
{
	LinkList s=(LinkList)malloc(sizeof(Node));
	if(s==NULL)
		return NULL;
	//创建成功
	memset(&s->cin,0,sizeof(s->cin));
	s->next=NULL;//表示创建新节点指针域为空
	return s;
}


int do_login(struct msgTyp msg, int sfd, struct sockaddr_in cin, LinkList head)
{
	
	//转发登录信息
	LinkList p = head;
	while(p != NULL)
	{
		sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(p->cin), sizeof(p->cin));
		p = p->next;
	}
	
	//将当前登录的用户的地址信息结构体存储起来
	LinkList s = Create();
	//新节点数据域即为它的地址信息结构体
	s->cin = cin;

	s->next=head->next;
	head->next=s;

	return 0;
}

int do_chat(LinkList head, int sfd, struct msgTyp msg, struct sockaddr_in cin)
{
	
	//转发群聊信息
	LinkList p = head;
	while(p != NULL)
	{

		if(p->cin.sin_port != cin.sin_port)   //端口号不一样说明不是同一个客户端,则将消息发给其他人
		{
			sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(p->cin), sizeof(p->cin));
		}
		p = p->next;
	}

	return 0;
}

int do_quit(LinkList head, int sfd, struct msgTyp msg, struct sockaddr_in cin)
{
	//转发离线信息
	LinkList p = head;
	while(p->next != NULL)
	{
		if(p->cin.sin_port != cin.sin_port)
		{
			p = p->next;
			sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(p->cin), sizeof(p->cin));
		}
		else	//将离线的客户端的地址信息结构体从链表中删除
		{
			LinkList del = p->next;
			p->next = del->next;
			free(del);
			del = NULL;
		}
	}
	return 0;
}

int main(int argc, const char *argv[])
{
	//判断是否外部传参
	if(argc != 2){
		printf("请输入IP地址!!!\n");
		return -1;
	}
	//创建套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		perror("socket");
		return -1;
	}
	printf("socket create success\n");

	
	//填充服务器自身的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET; 	
	sin.sin_port = htons(SER_PORT); 	//服务器绑定的端口号
	sin.sin_addr.s_addr = inet_addr(argv[1]); 	//服务器绑定的IP号

	//绑定
	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
	{
		perror("bind error");
		return -1;
	}
	printf("bind success\n");

	char buf[128] = "";
	int res = 0;

	struct sockaddr_in cin; 	//存储对端地址信息结构体
	socklen_t addrlen = sizeof(cin);

	LinkList head = Create();  //创建链表头结点,存储客户端信息

	struct msgTyp msg;   //接收数据的结构体存储位置

	pid_t pid = fork();  //创建子进程

	if(pid > 0)  //父进程
	{
		//接收数据
		while(1)
		{
			//接收客户端的消息
			res = recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cin, &addrlen);
			printf("[%s] %s\n",msg.name,msg.text);
			if(res < 0)
			{
				perror("recvfrom error");
				return -1;
			}
			//根据消息的类型实行不同功能
			switch(msg.type)
			{
			case 'L': 	
				do_login(msg, sfd, cin, head);
				break;
			case 'C':
				do_chat(head, sfd, msg, cin);
				break;
			case 'Q':
				do_quit(head, sfd, msg, cin);
				break;
			default:
				printf("error\n");break;
			}
		}
	}
	else if(0 == pid)  //子进程
	{
		//服务器可以发送系统消息
		msg.type = 'C';
		strcpy(msg.name, "系统消息");
		//发送数据
		while(1)
		{
		
			//清空消息正文
			memset(msg.text, 0, sizeof(msg.text));
			//从终端获取数据
			fgets(msg.text, sizeof(msg.text), stdin);
			msg.text[strlen(msg.text) - 1] = 0;

			sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin));	
		}
	}
	else 
	{
		perror("fork error");
		return -1;
	}
	//关闭所有文件描述符
	close(sfd);
	return 0;
}

客户端:

#include <myhead.h>

#define SER_PORT 8888

struct msgTyp  //消息结构体
{
    char type;      	//消息类型
    char name[30];  	//用户姓名
    char text[1024]; 	//消息正文
};

int main(int argc, const char *argv[])
{
	//判断是否外部传参
	if(argc!=2){
		printf("请输入IP地址\n");
		return -1;
	}

	//创建套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd ==-1)
	{
		perror("socket");
		return -1;
	}
	printf("socket create success\n");

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

	//输入上线的用户名称
	printf("请输入名字>>>");
	char name[20] = "";
	scanf("%s", name);
	getchar();

	//定义登录时的类型以及提示信息
	struct msgTyp msg_snd;
	msg_snd.type = 'L';
	strcpy(msg_snd.name, name);
	strcpy(msg_snd.text, "加入群聊");
	
	//发送登录信息
	if(sendto(sfd, &msg_snd, sizeof(msg_snd), 0, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
	{
		perror("sendto error");
		return -1;
	}

	char buf[128] = "";
	int res = 0;

	//存储对端地址信息结构体
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	//定义接收消息的结构体
	struct msgTyp msg_rcv;

	//创建子进程
	pid_t pid = fork();

	if(pid > 0)  //父进程
	{
		//发送数据,发送聊天信息
		while(1)
		{
			//客户端从终端获取消息并发送出去
			bzero(buf, sizeof(buf));                 //清空数组
			fgets(buf, sizeof(buf), stdin);
			buf[strlen(buf)-1] = 0;

			msg_snd.type = 'C';
			strcpy(msg_snd.text, buf);

			if(strcmp(msg_snd.text, "quit") == 0)
			{
				msg_snd.type = 'Q';
				strcpy(msg_snd.text, "已下线");
			}

			sendto(sfd, &msg_snd, sizeof(msg_snd), 0, (struct sockaddr*)&sin, sizeof(sin));

			if(strcmp(msg_snd.text, "已下线") == 0)
			{
				break;
			}
		}
		kill(pid,SIGKILL);
		wait(NULL);//等待回收子进程资源
	}
	else if(0 == pid)  //子进程
	{
		//接收数据
		while(1)
		{
			printf("\n");
			recvfrom(sfd, &msg_rcv, sizeof(msg_rcv), 0, NULL, NULL);
			printf("[%s] : %s", msg_rcv.name, msg_rcv.text);
		}
	}
	else
	{
		perror("fork error");
		return -1;
	}

	//关闭文件描述符
	close(sfd);

	return 0;
}

效果图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1498528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机考研】考408,还是不考408性价比高?

首先综合考虑&#xff0c;如果其他科目并不是很优秀&#xff0c;需要我们花一定的时间去复习&#xff0c;408的性价比就不高&#xff0c;各个科目的时间互相挤压&#xff0c;如果备考时间不充裕&#xff0c;考虑其他专业课也未尝不可。 复习408本来就是费力不讨好的事情 不同…

SAP MM学习笔记44 - 特殊调达流程 - Blanket购买发注(汇总采购)

上一章学习了 支付计划&#xff0c;本章继续学习 Blanket购买发注&#xff08;汇总采购&#xff09;。 SAP MM学习笔记43 - 特殊调达流程 - 支付计划-CSDN博客 1&#xff0c;Blanket购买发注 概要 其实就是订好一个大致数额&#xff0c;然后让随便买&#xff0c;只要不超这个…

网络调试助手使用MQTT协议与Mosquitto通信(3)

一、连接报文 一开始设备需要连接到mqtt服务器&#xff0c;连接时的数据包内需要携带对应的设备ID&#xff0c;以及用户名和密码。这使用默认的用户名和密码。设备ID每一个设备都需要设置为不同的&#xff0c;两个相同的ID只能允许一台设备在线&#xff0c;另一个相同的ID的设备…

2024年最新阿里云服务器地域选择方法,以及可用区说明

阿里云服务器地域和可用区怎么选择&#xff1f;地域是指云服务器所在物理数据中心的位置&#xff0c;地域选择就近选择&#xff0c;访客距离地域所在城市越近网络延迟越低&#xff0c;速度就越快&#xff1b;可用区是指同一个地域下&#xff0c;网络和电力相互独立的区域&#…

实操keepalived(高可用)+Nginx(四层代理+七层代理),实现高可用、负载均衡以及动静分离

一 vrrp技术 VRRP 相关术语 VRRP能够在不改变组网的情况下&#xff0c;将多台路由器虚拟成一个虚拟路由器&#xff0c;i通过配置虚拟路由器的IP地址为默认网关&#xff0c;实现网关的备份。 协议版本: VRRPv2 (常用) 和VRRPv3:0 VRRPv2仅适用于IPv4网络&#xff0c;VRRPv3适用…

好物周刊#46:在线工具箱

https://github.com/cunyu1943 村雨遥的好物周刊&#xff0c;记录每周看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;每周五发布。 一、项目 1. twelvet 一款基于 Spring Cloud Alibaba 的权限管理系统&#xff0c;集成市面上流行库&#xff0c;可以作用为快…

数字孪生10个技术栈:数据采集的八种方式

大家好&#xff0c;我是贝格前端工场&#xff0c;上期讲了数字孪生10个技术栈&#xff08;总括&#xff09;:概念扫盲和总体介绍&#xff0c;获得了大家的热捧&#xff0c;本期继续分享技术栈&#xff0c;大家如有数字孪生或者数据可视化的需求&#xff0c;可以联络我们。 一、…

文件包含漏洞初识

一、基础知识介绍 在web后台开发的时候&#xff0c;我们会使用PHP&#xff0c;Java这种代码&#xff0c;而在使用的过程中&#xff0c;我们经常会使用包含函数&#xff08;也就是调用&#xff09;&#xff0c;而很多时候&#xff0c;前端用户在选择浏览时会调用包含的文件这无…

LangChain自定义工具Tool

LangChain部署 pip install langchain自定义工具 from langchain_openai import ChatOpenAI from langchain.memory import ConversationBufferMemory from langchain.agents.conversational_chat.base import ConversationalChatAgent from langchain.agents import AgentEx…

常见四种限流算法详解(附:javaDemo)

限流简介 现代互联网很多业务场景&#xff0c;比如秒杀、下单、查询商品详情&#xff0c;最大特点就是高并发&#xff0c;而往往我们的系统不能承受这么大的流量&#xff0c;继而产生了很多的应对措施&#xff1a;CDN、消息队列、多级缓存、异地多活。 但是无论如何优化&…

Material Studio 中 DMol3 计算材料吸附能

1.先导入Cif文件 2.切表面 3.沿着你要切的晶面切 4.扩胞 5.加真空层&#xff08;一般加10埃&#xff09; 现在就是这样的了 6.然后对其结构优化&#xff08;高斯几何优化&#xff09; 7.再在体系上加原子或者想要的材料 8.Outmal文件中最后的Ef就是整个体系的能量&#xff0…

腾讯云哪款服务器最便宜划算?2024腾讯云服务器优惠价格表

腾讯云优惠活动2024新春采购节活动上线&#xff0c;云服务器价格已经出来了&#xff0c;云服务器61元一年起&#xff0c;配置和价格基本上和上个月没什么变化&#xff0c;但是新增了8888元代金券和会员续费优惠&#xff0c;腾讯云百科txybk.com整理腾讯云最新优惠活动云服务器配…

2024第二届语言,教育与艺术鉴赏国际会议(ICLEAA 2024)

2024第二届语言&#xff0c;教育与艺术鉴赏国际会议&#xff08;ICLEAA 2024&#xff09; 一、【会议简介】 我们非常荣幸地邀请您参加2024第二届语言&#xff0c;教育与艺术鉴赏国际会议&#xff08;ICLEAA 2024&#xff09;&#xff0c;该会议将在美丽的苏州举行。 ICLEAA …

Mybatis-Plus Mapper映射文件使用

介绍 MyBatis 的真正强大在于它的语句映射&#xff0c;这是它的魔力所在。由于它的异常强大&#xff0c;映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比&#xff0c;你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本&#xff0…

一篇文章带你通关并查集(持续更新中)

这篇文章的所有题目均来自于自行整理&#xff0c;代码均来自于自行梳理调试&#xff08;思路可能比较暴力&#xff09;。初衷在于整理练习思路&#xff0c;且起到督促自己学习的作用 本文分成将三个模块 1.普及组 &#xff08;洛谷黄题&#xff09; 2.提高组 &#xff08;洛…

OracleXE112、plsqldev1207的安装和基本配置

OracleXE112、plsqldev1207的安装和基本配置 OracleXE112、plsqldev1207的安装和基本配置Oracle安装oracle是什么Oracle两个版本下载安装包 安装OracleXE112_Win64注意&#xff1a;安装到空目录下&#xff1b;输入口令&#xff08;记住啊&#xff01;&#xff09;安装成功&…

基于springboot+vue的在线远程考试系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

别再盲目推广!用Xinstall精准追踪App下载渠道

在移动互联网时代&#xff0c;App推广已经成为企业获取用户、提升品牌知名度的重要手段。然而&#xff0c;面对众多的推广渠道和复杂的用户行为&#xff0c;如何精准统计App下载渠道来源&#xff0c;成为了广告主和开发者亟待解决的问题。今天&#xff0c;我们就来聊聊国内专业…

我的创作周年纪念日

机缘 最初成为创作者的初心&#xff1a;整理自己的知识体系&#xff0c;普及前端知识 实战项目中的经验分享日常工作学习过程中的记录通过文章进行技术交流归纳和整理自己的知识体系 收获 创作的过程中收获&#xff1a; 获得了909粉丝的关注获得了很多正向的反馈&#xff0c…

Leetcoder Day42| 动态规划part09 打家劫舍问题

198.打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房…