网络编程day6作业

完成网络聊天室编写

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.hqwc.cn/news/100381.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab 计算矩阵的条件数

目录 一、概述1、算法概述2、主要函数3、参考文献二、代码实现三、结果展示四、参考链接本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、概述 1、算法概述 条件数法是目前应用最为广泛的一种病态诊断方法。一个方阵…

npm修改为国内镜像

npm config get registry 查看下载地址&#xff0c;默认是https://registry.npmjs.org/ 这是国外的地址&#xff0c;npm下载很慢 npm config set registry https://registry.npmmirror.com/ 使用此指令&#xff0c;修改为淘宝镜像&#xff0c;输入指令后检查是否修改成功 …

[当人工智能遇上安全] 8.基于API序列和机器学习的恶意家族分类实例详解

您或许知道&#xff0c;作者后续分享网络安全的文章会越来越少。但如果您想学习人工智能和安全结合的应用&#xff0c;您就有福利了&#xff0c;作者将重新打造一个《当人工智能遇上安全》系列博客&#xff0c;详细介绍人工智能与安全相关的论文、实践&#xff0c;并分享各种案…

ETCD详解

一、etcd概念 ETCD 是一个高可用的分布式键值key-value数据库&#xff0c;可用于服务发现。 ETCD 采用raft 一致性算法&#xff0c;基于 Go语言实现。 etcd作为一个高可用键值存储系统&#xff0c;天生就是为集群化而设计的。由于Raft算法在做决策时需要多数节点的投票&…

华为数据管理——《华为数据之道》

数据分析与开发 元数据是描述数据的数据&#xff0c;用于打破业务和IT之间的语言障碍&#xff0c;帮助业务更好地理解数据。 元数据是数据中台的重要的基础设施&#xff0c;元数据治理贯彻数据产生、加工、消费的全过程&#xff0c;沉淀了数据资产&#xff0c;搭建了技术和业务…

【Arduino24】8*8点阵实验

硬件准备 8*8点阵&#xff1a;1个 旋钮电位器&#xff1a;1个 面包板&#xff1a;1块 杜邦线&#xff1a;若干 硬件连线 软件程序 //定义引脚 #define xKnob_pin A0 //x轴旋钮的引脚 #define yKnob_pin A1 //y轴旋钮的引脚 const int row_pin[8] { 6, 11, 10, 3, 17, 4…

pandas(四十三)Pandas实现复杂Excel的转置合并

一、Pandas实现复杂Excel的转置合并 读取并筛选第一张表 df1 pd.read_excel("第一个表.xlsx") df1# 删除无用列 df1 df1[[股票代码, 高数, 实际2]].copy() df1df1.dtypes股票代码 int64 高数 float64 实际2 int64 dtype: object读取并处理第二张表…

李宏毅-21-hw3:对11种食物进行分类-CNN

一、代码慢慢阅读理解总结内化&#xff1a; 1.关于torch.nn.covd2d()的参数含义、具体用法、功能&#xff1a; &#xff08;1&#xff09;参数含义&#xff1a; 注意&#xff0c;里面的“padding”参数&#xff1a;《both》side所以是上下左右《四》边都会加一个padding数量…

sqlserver数据库链接mysql服务器访问数据

sqlserver数据库链接mysql服务器访问数据 关于SqlServer数据库怎么链接mysql数据库我一直不明白&#xff0c;今天项目碰到一个问题需要链接&#xff0c;我就研究了一下&#xff0c;然后就成功了&#xff0c;在这里记录一下。也欢迎朋友互相学习交流借鉴。 1.使用navicat打开S…

使用Maven创建父子工程

&#x1f4da;目录 创建父工程创建子模块创建子模块示例创建认证模块(auth) 结束 创建父工程 选择空项目&#xff1a; 设置&#xff1a;项目名称&#xff0c;组件名称&#xff0c;版本号等 创建完成后的工程 因为我们需要设置这个工程为父工程所以不需要src下的所有文件 在pom…

学习笔记|计数器|Keil软件中 0xFD问题|I/O口配置|STC32G单片机视频开发教程(冲哥)|第十二集:计数器的作用和意义

文章目录 1.计数器的用途2.计数器的配置官方例程开始Tips&#xff1a;编译时提示错误FILE DOES NOT EXIST&#xff1a; 3.计数器的应用本例完整代码&#xff1a;总结课后练习&#xff1a; 1.计数器的用途 直流有刷的电机,后面两个一正一负的电接上,电机就可以转 到底是转子个…

Zoom正式发布类ChatGPT产品—AI Companion

9月6日&#xff0c;全球视频会议领导者Zoom在官网宣布&#xff0c;正式发布生成式AI助手——AI Companion。 AI Companion提供了与ChatGPT类似的功能&#xff0c;包括根据文本对话起草各种内容&#xff0c;自动生成会议摘要&#xff0c;自动回答会议相关问题等&#xff0c;以帮…