网络编程作业day7

news2024/10/24 14:22:47

作业项目:基于UDP的聊天室

服务器代码:

#include <myhead.h>

//定义客户信息结构体
typedef struct magtye
{
	char type;            //消息类型
	char name[100];         //客户姓名
	char text[1024];  //客户发送聊天信息
}msg_t;

//定义结构体存储每个客户端的ip地址和端口号
typedef struct IP_PORT
{
	struct sockaddr_in cin;//地址信息
	struct IP_PORT *next;//

}*addrlist;

void usr_login(int sfd,msg_t msg,addrlist *head,struct sockaddr_in cin);
void usr_chat(int sfd,msg_t msg,addrlist head,struct sockaddr_in cin);
void usr_quit(int sfd,msg_t msg,addrlist *head,struct sockaddr_in cin);

int main(int argc, const char *argv[])
{
	//创建套接字
	int sfd=-1;
	sfd=socket(AF_INET,SOCK_DGRAM, 0);
	if(sfd==-1)
	{
		perror("socket error");
		return -1;
	}
	//将端口号快速重用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
	{
		perror("setsockopt error");
		return -1;
	}

	//服务器进行绑定
	//(1)、从终端获取端口号和地址
	char SER_IP[100];
	int SER_PORT;
	printf("请输入服务器ip地址和端口号:");
	scanf("%s %d",SER_IP,&SER_PORT);
	getchar();//吸收垃圾字符

	//(2)、填充服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;           //地址族
	sin.sin_port=htons(SER_PORT);    //端口号
	sin.sin_addr.s_addr=inet_addr(SER_IP);  //IP地址      	
	socklen_t sin_len=sizeof(sin);
	//(3)、绑定
	if(bind(sfd,(struct sockaddr*)&sin,sin_len)==-1)
	{
		perror("bind error");
		return -1;
	}
	//定义客户端网络信息结构体
	struct sockaddr_in cin;
	socklen_t cin_len=sizeof(cin);

	msg_t msg;//定义客户发送消息的机构体变量

	//创建父子进程处理不同的操作
	int pid=fork();
	if(pid==-1)
	{
		perror("fork error");
		return -1;
	}
	if(pid==0)//字进程实现服务器接收消息类型
	{
		addrlist head=NULL;//链表头指针;
		while(1)
		{
			memset(&msg,0,sizeof(msg));
			memset(&cin,0,sizeof(cin));
			//接收客户端发来的信息,并判断属于哪种消息类型
			recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&cin_len);
			switch(msg.type)
			{
			case 'L':    //该消息类型代表新用户上线
				{
					//创建新节点保存新用户的ip地址和端口号				
					//向其他在线的用户发送新用户上线通知
					usr_login(sfd,msg,&head,cin);

				}break;
			case 'C':   //该消息类型代表一个用户发送消息给其他用户
				{
					usr_chat(sfd,msg,head,cin);
				}break;
			case 'Q':   //该消息类型代表一个用户下线
				{
					usr_quit(sfd,msg,&head,cin);
				}break;
			}
		}
	}
	else if(pid>0)//父进程实现服务器对客户端发送消息
	{
		strcpy(msg.name,"服务器消息");
		msg.type='C';
		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,sin_len);
			if(strcmp(msg.text,"服务器下线")==0)
			{
				sleep(1);
				break;
			}
		}
		kill(pid,SIGKILL);//服务器下线杀死子进程
	}
	wait(NULL);
	//关闭套接字
	close(sfd);
	return 0;
}
//用户登录操作函数
void usr_login(int sfd,msg_t msg,addrlist *head,struct sockaddr_in cin)
{
	//1、创建新节点
	addrlist s=(addrlist)malloc(sizeof(addrlist));
	s->next=NULL;
	//存储新用户的ip地址和端口号
	printf("%s : %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
	s->cin=cin;
	s->next=*head;
	*head=s;
	//2、遍历链表将新用户上线消息发送给其他在线用户
	addrlist 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; //后移
	}
	printf("%s:%s\n",msg.name, msg.text);

}

//用户聊天操作函数
void usr_chat(int sfd,msg_t msg,addrlist head,struct sockaddr_in cin)
{
	addrlist 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; //后移
	}

}
//用户退出操作函数
void usr_quit(int sfd,msg_t msg,addrlist *head,struct sockaddr_in cin)
{
	printf("%s:%s\n",msg.name, msg.text);
	addrlist p=*head;
	addrlist del=NULL;
	while (p!=NULL)
	{
		if(p->cin.sin_port!=cin.sin_port)  //向其他用户发送某个用户下线消息
		{
			sendto(sfd, &msg, sizeof(msg),0,(struct sockaddr *)&(p->cin), sizeof(p->cin));
			del=p;
			p=p->next;
		}
		else
		{
			sendto(sfd, &msg, sizeof(msg),0,(struct sockaddr *)&(p->cin), sizeof(p->cin));//向发出下线消息的用户回复消息
			if (del==NULL) 
			{
				*head=p->next;
			} 
			else 
			{
				del->next=p->next;
			}
			free(del);
			del=NULL;
			break;
		}
	}
}

客户端代码:

#include <myhead.h>

//定义客户信息结构体
typedef struct magtye
{
	char type;            //消息类型
	char name[100];         //客户姓名
	char text[1024];  //客户发送聊天信息
}msg_t;

int main(int argc, const char *argv[])
{
	//创建套接字
	int cfd=-1;
	cfd=socket(AF_INET, SOCK_DGRAM, 0);
	if(cfd==-1)
	{
		perror("socket error");
		return -1;
	}
	//将端口号快速重用
	int reuse=1;
	if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
	{
		perror("setsockopt error");
		return -1;
	}

	//(1)、从终端获取端口号和地址
	char SER_IP[100];
	int SER_PORT;
	printf("请输入服务器ip地址和端口号:");
	scanf("%s %d",SER_IP,&SER_PORT);
	getchar();//吸收垃圾字符

	//(2)、填充服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;           //地址族
	sin.sin_port=htons(SER_PORT);    //端口号
	sin.sin_addr.s_addr=inet_addr(SER_IP);  //IP地址   
	socklen_t sin_len=sizeof(sin);
	msg_t msg;

	//客户端上线发送消息
	printf("请输入用户名:");
	fgets(msg.name,sizeof(msg.name),stdin);
	msg.name[strlen(msg.name)-1] = '\0';
	strcpy(msg.text,"已上线");
	msg.type='L';
	sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len);

	//创建多进程进行收发消息操作
	int pid=fork();
	if(pid==-1)
	{
		perror("fork error");
		return -1;
	}
	 if(pid>0)//父进程进行读取消息
	{
		while(1)
		{
			recvfrom(cfd,&msg,sizeof(msg),0,NULL,NULL);
			if(strcmp(msg.text,"退出群聊")==0)    //用户自己下线
			{
				break;
			}
			printf("[%s]: %s\n", msg.name, msg.text);
			if(strcmp(msg.text,"服务器下线")==0) //服务器让客户端下线
			{
				kill(pid,SIGKILL);
				break;
			}
		}
	}
	else if(pid==0)//子进程发送消息
	{
		while(1)
		{
			memset(msg.text,0,sizeof(msg.text));
			fgets(msg.text,sizeof(msg.text),stdin);//在终端获取聊天信息
			msg.text[strlen(msg.text)-1]='\0';
			if(strcmp(msg.text, "下线")==0)
			{
				msg.type='Q';
				strcpy(msg.text, "退出群聊");
			}
			else
			{
				msg.type='C';
			}
			sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len);
			if(strcmp(msg.text,"退出群聊")==0)
			{
				break;
			}
		}
		exit(EXIT_SUCCESS);
	}
	//关闭套接字回收子进程资源
	wait(NULL);   //阻塞回收子进程资源
	close(cfd);
	return 0;
}

项目作业-基于udp的聊天室

思维导图:

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

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

相关文章

基于YOLOv5的驾驶员疲劳驾驶行为​​​​​​​检测系统

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文主要内容:详细介绍了疲劳驾驶行为检测整个过程&#xff0c;从数据集到训练模型到结果可视化分析。 博主简介 AI小怪兽&#xff0c;YOLO骨灰级玩家&#xff0c;1&#xff09;YOLOv5、v7、v8优化创新&#xff0c;轻松涨点和模型轻量…

Java特性之设计模式【过滤器模式】

一、过滤器模式 概述 ​ 过滤器模式&#xff08;Filter Pattern&#xff09;或标准模式&#xff08;Criteria Pattern&#xff09;是一种设计模式&#xff0c;这种模式允许开发人员使用不同的标准来过滤一组对象&#xff0c;通过逻辑运算以解耦的方式把它们连接起来。这种类型的…

几何工具的使用

Geometry - Creation 创建几何 CogCreateCircleTool&#xff1a;创建圆CogCreateEllipseTool:创建椭圆CogCreateLineBisectPointsTool&#xff1a;带有两个点的平行线CogCreateLineParallelTool:在某一点创建某条线的平行线CogCreateLinePerpendicularTool:在某一点创建某条线…

STL中push_back和emplace_back效率的对比

文章目录 过程对比1.通过构造参数向vector中插入对象&#xff08;emplace_back更高效&#xff09;2.通过插入实例对象&#xff08;调用copy函数&#xff09;3.通过插入临时对象&#xff08;调用move函数&#xff09; 效率对比emplace_back 的缺点 我们以STL中的vector容器为例。…

力扣每日一题 找出字符串的可整除数组 数论

Problem: 2575. 找出字符串的可整除数组 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 灵神题解 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public int[] divisibilityArray(String word, int m){in…

外包干了一周,技术明显倒退。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;2019年我通过校招踏入了南京一家软件公司&#xff0c;开始了我的职业生涯。那时的我&#xff0c;满怀热血和憧憬&#xff0c;期待着在这个行业中闯出一片天地。然而&#xff0c;随着时间的推移&#xff0c;我发现自己逐渐陷入…

【JavaEE初阶】 JVM 运行时数据区简介

文章目录 &#x1f343;前言&#x1f332;堆&#xff08;线程共享&#xff09;&#x1f384;Java虚拟机栈&#xff08;线程私有&#xff09;&#x1f38b;本地方法栈&#xff08;线程私有&#xff09;&#x1f333;程序计数器&#xff08;线程私有&#xff09;&#x1f334;方法…

ospf虚链路实验简述

1、ospf虚链路实验简述 ospf虚链路配置 为解决普通区域不在骨干区域旁&#xff0c;通过配置Vlink-peer实现不同区域网络设备之间建立逻辑上的连接。 实验拓扑图 r1: sys sysname r1 undo info enable int loopb 0 ip add 1.1.1.1 32 ip add 200.200.200.200 32 quit int e0/0/…

京东老矣,尚能饭否?

图片&#xff5c;《冰与火之歌》截图 ©自象限原创 作者丨程心 编辑丨罗辑 从2004年1月&#xff0c;京东正式涉足电商至今&#xff0c;整整二十年过去了。 2024年3月6日&#xff0c;京东发布了2023年第四季度及全年财报。数据显示&#xff0c;2023Q4京东收入3061亿元人民…

【软件测试】如何申请专利?

一、专利类型 在软件测试领域&#xff0c;可以申请发明专利、实用新型专利和外观设计专利。其中&#xff0c;发明专利是最常见的专利类型&#xff0c;它保护的是软件测试方法、系统和装置等技术方案。 二、申请专利的条件 申请专利需要满足新颖性、创造性和实用性三个条件。…

【Linux】开始使用gdb吧!

开始使用gdb吧&#xff01; 1 下载安装2 开始使用3 实践运用补充一下 print 的 功能 &#xff08;类似监视窗口的作用&#xff09;和显示堆栈的功能Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读&#xff01;&#xff01;&#xff01;下一篇文章见&#xff01;&am…

【力扣白嫖日记】1070.产品销售分析III

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 1070.产品销售分析III 表&#xff1a;Sales 列名类型sale_idintproduct_idintyearintquantityintpriceint …

01背包问题 刷题笔记

思路 dp 用f[i][j]来表示当体积为j时 考虑前i件物品可以获得的 最大值 记住f[i][j]本身是个价“价值” 考虑两种状态 是否将第i件物品放入背包里面 将背包的体积从小到大递增来进行考虑 首先 考虑条件 如果当前增加的体积放不下下一件物品 则该体积 可以获得的最大值可以直接…

Easticsearch性能优化之索引优化

Easticsearch性能优化之索引优化 一、合理的索引设计二、合理的分片和副本三、合理的索引设置 对于性能优化&#xff0c;Elasticsearch&#xff08;以下简称ES&#xff09;的索引优化是提高性能的关键因素之一。合理的设计索引&#xff0c;合理的分片和副本以及合理的缓存设置等…

Truenas入门级教程

Truenas入门教程 前言&#xff1a;系统相关配置 采用I3 4160&#xff0c;采用了2块500G的硬盘&#xff0c;内存为8G&#xff0c;两个网卡只用了其中一个&#xff0c;系统安装的是core版本 硬件采用DELL3020MT机箱&#xff0c;自带3个SATA网口&#xff0c;后期网口不够&#…

七.AV Foundation 视频播放 - 图片进度条

引言 播放器的功能功能已经十分完善了&#xff0c;接下来我们给它添加一些提升用户体验的功能。当前市面上的主流播放器几乎都有一个非常友善的功能&#xff0c;用户在退拽进度条的时候可以看见进度条所处进度的视频画面&#xff0c;这对于用户来说是一种直观而且便捷的体验。…

今天分享一个好看的输入法皮肤相信每个人心里住着一个少女心我们美化一下她吧

标题&#xff1a; 白日梦皮肤上线&#xff0c;百度输入法助你开启梦幻之旅&#xff01; 正文&#xff1a; 大家好呀&#xff01;今天我来给大家安利一款超级梦幻的百度输入法皮肤——“白日梦”系列&#xff01; 这款皮肤的设计灵感来源于我们内心深处的白日梦&#xff0c;充…

【Python】新手入门(9):数值和序列

&#x1f40d;【Python】新手入门&#xff08;9&#xff09;&#xff1a;数值和序列 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&am…

MySQL为什么要用B+树?

二叉树&#xff08;二叉查找树&#xff09; 平衡二叉树&#xff08;B树就是B-树&#xff09;(解决了二叉查找树的极端情况&#xff09; Q&#xff1a;具体是怎么解决的呢&#xff1f; A&#xff1a; 树左右两边层数相差不大于1一旦符合条件1的时候&#xff0c;就进行左旋/右…

重装系统后正版office如何安装

前言 重装系统后&#xff0c;正版office如何安装 登录官网 https://www.microsoft.com 下载office https://account.microsoft.com/services