网络编程day6作业

news2025/1/12 23:16:38

完成网络聊天室编写

ser

#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__",__LINE__);\
	perror(msg);\
}while(0)
#define IP "127.0.0.1"
#define PORT 6666
//创建链表
Linklistptr list_create();
Linklistptr node_buy(datatype e);
int list_insert_head(Linklistptr L,datatype e);
int chat_login(Linklistptr L,int sfd,msg_t msg,datatype e);
int chat_enter(Linklistptr l,int sfd,msg_t msg,datatype e);
int chat_exit(Linklistptr L,int sfd,msg_t msg,datatype e);
typedef struct sockaddr_in datatype;//类型重命名
//创建信息结构体
typedef struct msg
{
	char type;//操作码 'L'登录 'C'群聊 'Q'退出
	char name[20];
	char text[128];
}msg_t;
//创建链表保存地址信息
typedef struct Node
{
	union
	{
		datatype res_addr;//数据域
		int len;//头结点数据域
	};
	struct Node *next;//指针域
}Node, *Linklistptr;
int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success sfd=%d\n",sfd);
	//填充接收方的地址信息结构体,给bind函数使用
	datatype 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;
	}
	printf("bind success");
	//创建接收地址信息结构体
	datatype cin;
	socklen_t addrlen = sizeof(cin);
	pthread_t tid;
	msg_t msg;
	//创建一个链表
	Linklistptr L = list_create();
	if(NULL == L)
	{
		return -1;
	}
	while(1)
	{
		//主线程负责接收并处理
		if(recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cin, &addrlen) < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		printf("[%s:%d] : %s\n", msg.name, ntohs(cin.sin_port), msg.text);
		switch(msg.type)
		{
		case 'L'://登录
			chat_login(L,sfd,msg,e);
			break;
		case 'C'://群聊
			chat_enter(L,sfd,msg,e);
			break;
		case 'Q'://退出
			chat_exit(L,sfd,msg,e);
			break;
		default :
			printf("输入错误\n");
			break;
		}
		info.sfd=sfd;
		info.sin=sin;
		//分支线程只负责发送系统信息
		if(pthread_create(&tid, NULL, task,(void*)&info) != 0)
		{
			fprintf(stderr, "pthread_create  failed__%d__\n",__LINE__);
			return -1;
		}
		pthread_detach(tid);
	}
	//关闭文件描述符
	if(close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}
void *task(void *arg)
{
	int sfd = ((struct Climsg*)arg)->sfd;
	datatype sin = ((struct Climsg*)arg)->sin;
	msg_t msg;
	msg.type = 'C';

	while(1)
	{

		//从终端获取消息文本
		fgets(msg.text, sizeof(msg.text), stdin);
		msg.text[strlen(msg.text)-1] = '\0';
		//将信息包名定为服务器
		strcpy(msg.name,"servce");
		if(sendto(sfd , &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
		{
			ERR_MSG("sendto");
		}
	}
	close(sfd);
	pthread_exit(NULL);
}
//创建链表
Linklistptr list_create()
{
	//从堆区申请一个头结点类型
	Linklistptr L = (Linklistptr)malloc(sizeof(Node));
	if(NULL == L)
	{
		printf("创建失败\n");
		return NULL;
	}
	//创建成功后,对节点进行初始化工作
	L->len = 0;
	L->next = NULL;
	return L;
}
//申请节点封装地址信息
Linklistptr node_buy(datatype e)
{
	//在堆区申请节点
	Linklistptr p = (Linklistptr)malloc(sizeof(Node));
	if(NULL == p)
	{
		printf("申请失败]n");
		return NULL;
	}
	//节点申请成功,封装数据
	p->res_addr = e;
	p->next = NULL;
	return p;
}
//头插
int list_insert_head(Linklistptr L,datatype e)
{
	//判断逻辑
	if(NULL == L)
	{
		printf("所给链表不合法\n");
		return -1;
	}
	//调用节点封装数据
	
	Linklistptr p = node_buy(e);
	if(NULL == p)
	{
		return -1;
	}
	//头插
	p->next = L->next;
	L->next = p;
	//表长变化
	L->len++;
	return 0;
}
int chat_login(Linklistptr L,int sfd,msg_t msg,datatype e)
{
	Linklistptr p=L;//定义一个遍历指针
	while(p->next!=NULL)
	{
		p=p->next;
		if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr *)&(p->res_addr),sizeof(p->res_addr))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
	}
	//头插,将自己的地址存入链表中
	list_insert_head(L,e);
	return 0;
}
//群聊
int chat_enter(Linklistptr l,int sfd,msg_t msg,datatype e)
{
	//定义一个遍历指针
	Linklistptr p=L;
	while(p->next!=NULL)
	{
		p=p->next;
		//判断链表客户端信息
		if(memcmp(&(p->res_addr),&e,sizeof(e))==0)
		{
			if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr*)&(p->res_addr),sizeof(p->res_addr))<0)
			{
				ERR_MSG("sendto");
				return -1;
			}
		}
	}
	return 0;
}
//退出
int chat_exit(Linklistptr L,int sfd,msg_t msg,datatype e)
{
	Linklistptr p=L;
	while(p->next!=NULL)
	{	
		p=p->next;
		//判断链表客户端信息
		if(memcmp(&(p->res_addr),&e,sizeof(e))==0)
		{
			if(sendto(sfd,&msg,sizeof(msg_t),0,(struct sockaddr*)&(p->res_addr),sizeof(p->res_addr))<0)
			{
				ERR_MSG("sendto");
				return -1;
			}
		}
		else//此时当前节点的下一个节点保存的就是要退出的成员的信息
		{
			Linklistptr q=p->next;
			if(sendto(sfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&(q->resin), sizeof(q->resin)) < 0)
			{
				ERR_MSG("sendto");
				return -1;
			}
			p->next=q->next;
			free(q);
			q=NULL;
		}
	}
	return 0;
}

cli

#include <myhead.h>
#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__",__LINE__);\
	perror(msg);\
}while(0)
#define IP "127.0.0.1"
#define PORT 4399
typedef struct sockaddr_in datatype;//类型重命名
//创建信息结构体
typedef struct msg
{
	char type;//操作码 'L'登录 'C'群聊 'Q'退出
	char name[20];
	char text[128];
}msg_t;

struct Climsg
{
	int cfd;
	datatype cin;
	msg_t msg;
};
void *task(void *arg);
int main(int argc, const char *argv[])
{
		//创建报式套接字
	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(cfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success cfd=%d\n",cfd);
	//填充接收方的地址信息结构体,给sendto函数使用
	datatype sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);
	//创建接收地址信息结构体
	datatype cin;
	socklen_t c_addrlen = sizeof(cin);	
	pthread_t tid;
	msg_t msg;
	memset(&msg, 0, sizeof(msg_t));
	struct Climsg info;
	printf("请输入登录名:");
	fgets(msg.name, sizeof(msg.name), stdin);
	msg.name[strlen(msg.name)-1] = '\0';	
	msg_t msg1=msg;
	msg.type = 'L';
	strcpy(msg.text,"已进入群聊");
	//发送登录请求包
	if(sendto(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	printf("登陆成功\n");
	info.cfd = cfd;
	info.cin = sin;
	info.msg = msg;	
	while(1)
	{
		
		memset(&msg, 0, sizeof(msg));
		if(recvfrom(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&cin, &c_addrlen) < 0)
		{
			ERR_MSG("recvfrom");
			return -1;
		}
		if(strcmp(msg.name, msg1.name) == 0)
		{
			break;
		}

		printf("[%s] : %s\n", msg.name, msg.text);


		if(pthread_create(&tid, NULL, task, (void *)&info) != 0)
		{
			ERR_MSG("pthread_create");
			return -1;
		}
	
		//将分支线程设置为分离态
		pthread_detach(tid);
		}

	//关闭套接字描述符
	close(cfd);
	return 0;
}
//线程体函数
void *task(void *arg)
{

	int cfd = ((struct Climsg*)arg)->cfd;
	datatype sin = ((struct Climsg*)arg)->cin;
	msg_t msg = ((struct Climsg*)arg)->msg;

	while(1)
	{
		//清空文本内容
		bzero(msg.text, sizeof(msg.text));
		//从终端获取数据
		fgets(msg.text, sizeof(msg.text), stdin);
		msg.text[strlen(msg.text)-1] = '\0';

		msg.type = 'C';
		if(strcmp(msg.text,"quit") == 0)
		{
			msg.type = 'Q';
			strcpy(msg.text, "已下线");
		}
		if(sendto(cfd, &msg, sizeof(msg_t), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0)
		{
			ERR_MSG("sendto");
		}
		if(strcmp(msg.text, "已下线") == 0)
		{
			break;
		}
	}
	close(cfd);
	pthread_exit(NULL);
}

思维导图:https://mubu.com/app/edit/home/5fnWgXpb5GT#m

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

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

相关文章

【三维重建】【深度学习】【数据集】基于COLMAP制作个人Gen6D测试数据集

【三维重建】【深度学习】【数据集】基于COLMAP制作个人Gen6D测试数据集 提示:最近开始在【三维重建】方面进行研究,记录相关知识点,分享学习中遇到的问题已经解决的方法。 文章目录 【三维重建】【深度学习】【数据集】基于COLMAP制作个人Gen6D测试数据集前言下载安装colmap软…

龙迅LT86102UX HDMI一进二出,支持分辨率4K60HZ

龙迅LT86102UXE 1. 描述 龙迅LT86102UX HDMI2.0 分路器具有符合 HDMI2.0/1.4 规范的 1&#xff1a;2 分路器、最大 6Gbps 高速数据速率、自适应均衡 RX 输入和预强调的 TX 输出&#xff0c;支持长电缆应用&#xff0c;板载无 XTAL&#xff0c;可节省 BOM 成本。 LT86102UX HDM…

海保人寿:开源治理保障科技与保险融合,助力保险业务数字化改革创新

海保人寿保险股份有限公司&#xff08;简称“海保人寿”&#xff09;是第一家在海南筹建开业的全国性保险机构。从成立之初&#xff0c;便深耕于数字化创新&#xff0c;在自身多业务环节中实现数字化转型&#xff0c;依托优秀的研发体系与数智融合的业务系统&#xff0c;不断推…

MAYA材质学习(各种好看的)

例子2 例子3 例子4 例子5 例子6 例子7

如何解决GitHub 访问不了?小白教程

GitHub 是全球最大的代码开源平台&#xff0c;小伙伴们平时都喜欢在那里找一些优质的开源项目来学习&#xff0c;以提升自己的编程技能。 但是很多小白初探GitHub 发现访问不了&#xff0c;不能访问 通过一下方法绕过这堵墙&#xff0c;成功下载 GitHub 上的项目。过程非常简单…

Ukey连接虚拟前置机,浦银安盛基金用USB Server解决

浦银安盛基金一直使用物理前置机来连接Ukey&#xff0c;物理前置机维护管理不便&#xff0c;严重影响金融交易效率。因此&#xff0c;浦银安盛基金想要推进企业虚拟化&#xff0c;通过在虚拟机中部署前置机程序的方式&#xff0c;将前置机部署到虚拟机中。但虚拟机中的前置机&a…

SpringCloud整体架构概述

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; SpringCloud整体架构概述 SpringCloud对常见的分布式系统模式提供了简单易用的编程模型&#xff0c;帮助开发者构建弹性、可靠、协调的应用程序。 SpringCloud是在Spr…

在线升级之ICAP,ISP,BIN,HEX,MCS

学到了ICAP&#xff0c;做做笔记。 具体可参考&#xff1a;FPGA中ICAP原语的使用——Multiboot功能的实现_朽月的博客-CSDN博客 ICAP是管理内部配置访问端口的原语&#xff0c;于是我学习的第一个原语。对于原语的概念简而言之就是类似IP核的存在&#xff0c;是既成的电路结构&…

使用Puppeteer爬取地图上的用户评价和评论

导语 在互联网时代&#xff0c;获取用户的反馈和意见是非常重要的&#xff0c;它可以帮助我们了解用户的需求和喜好&#xff0c;提高我们的产品和服务质量。有时候&#xff0c;我们需要从地图上爬取用户对某些地点或商家的评价和评论&#xff0c;这样我们就可以分析用户对不同…

C++内存管理(2)new、delete详解

目录 new operator&#xff08;new操作&#xff09; new类对象时加不加括号的差别 new工作任务 delete工作任务 new和delete 堆区空间操作&#xff08;对比malloc和free&#xff09; new和delete操作基本类型的空间 new和delete操作基本类型的数组 new和delete操作类的…

Layui快速入门之第二节布局容器(固定宽度与完整宽度)

目录 一&#xff1a;固定宽度 二&#xff1a; 完整宽度 一&#xff1a;固定宽度 将栅格放入一个带有 class"layui-container" 的特定容器中&#xff0c;以便在小屏幕以上的设备中固定宽度&#xff0c;让列可控(两侧有留白效果) <!--固定宽度(两侧有留白效果)--&…

Layui快速入门之第三节栅格布局

目录 一&#xff1a;栅格布局的基本概念 二&#xff1a;栅格布局规则 三&#xff1a;始终等比例水平排列案例 四&#xff1a;响应式规则 五&#xff1a;移动设备、桌面端的组合响应式展现案例 六&#xff1a;移动设备、平板、桌面端的复杂组合响应式展现案例 七&#xf…

Yalmip使用教程(6)-将约束条件写成矩阵形式

博客中所有内容均来源于自己学习过程中积累的经验以及对yalmip官方文档的翻译&#xff1a;https://yalmip.github.io/tutorials/ 这篇博客将详细介绍如何借助yalmip工具箱将约束条件写成矩阵形式。 1.相关函数介绍 1.1 depends和getvariables函数 depends和getvariables函数都…

C# Winform 简单排期实现(DevExpress TreeList)

排期的需求在很多任务安排的系统中都有相应的需求&#xff0c;原生的Winform控件并未提供相应的控件&#xff0c;一般都是利用DataGridViewTreeView组合完成相应的需求&#xff0c;实现起来比较麻烦。用过DevExpress控件集的开发者应该知道&#xff0c;DevExpress WinForm提供了…

数学建模--K-means聚类的Python实现

目录 1.算法流程简介 2.1.K-mean算法核心代码 2.2.K-mean算法效果展示 3.1.肘部法算法核心代码 3.2.肘部法算法效果展示 1.算法流程简介 #k-means聚类方法 """ k-means聚类算法流程: 1.K-mean均值聚类的方法就是先随机选择k个对象作为初始聚类中心. 2.这…

http实现文件分片下载

文章目录 检测是否支持HTTP Range 语法Range请求cURL示例单一范围多重范围条件式分片请求 Range分片请求的响应文件整体下载文件分片下载文本下载图片下载封装下载方法 HTTP分片异步下载是一种下载文件的技术&#xff0c;它允许将一个大文件分成多个小块&#xff08;分片&#…

一个新工具 nolyfill

名字的意思&#xff0c; 我自己的理解 no(po)lyfill 正如它的名字, 不要再用补丁了, 当然这里说的是过时的补丁。 polyfill 是补丁的意思 为什么要用这个插件 文档原文: 当您通过安装最新的 Node.js LTS 来接受最新的功能和安全修复时&#xff0c;像eslint-plugin-import、…

架构师如何做好需求分析

架构师如何做好需求分析 目录概述需求&#xff1a; 设计思路实现思路分析1.主要步骤 2.主要步骤2操作步骤 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,…

Android笔记(二十八):在雷电模拟器安卓7.0+上使用Charles抓包详细教程

背景 由于手头没有合适的真机,所有经常使用雷神模拟器来跑项目,模拟器也需要能够抓包看看接口返回的数据,以便自测调试。本文记录了如何在雷电模拟器安卓7.0+上使用Charles抓包,其他模拟器没试过。 最终效果 浏览器打开百度网页,能抓到百度页面数据 具体步骤 模拟器…

xinput1_3.dll丢失的解决方法,快速修复xinput1_3.dll文件

在使用电脑时&#xff0c;我们可能会遇到各种各样的问题&#xff0c;其中之一就是xinput1_3.dll文件丢失的情况。这个文件是DirectX的一部分&#xff0c;它对于许多游戏和其他应用程序的正常运行至关重要。当xinput1_3.dll文件丢失时&#xff0c;我们无法启动或运行依赖该文件的…