华清远见作业第三十一天——网络编程(第六天)

思维导图:

 代码:

服务器:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <a.h>
#define SER_PORT 10000
#define login 1 //登录协议
#define exchange 2 //交流协议
#define quit 3 //退出协议//定义从客户端发来信息的结构体体内容
typedef struct infor
{int type_num; //协议char name[50]; //名字char text[128]; //发送的内容
}type_s;
//定义结构体接收客户端地址信息结构体
typedef struct cli_infor
{struct sockaddr_in cin; //该结构体中包含了客户端通信区域,端口号,ip地址struct cli_infor *next; //指向下一个位置
}cli_linklist;
int do_login(int sfd, cli_linklist *head,struct infor rcv_info,struct sockaddr_in cin);
//父进程接收客户端信息函数
int do_recv(int sfd);
int do_exchange(int sfd,cli_linklist *head,struct infor rcv_info,struct sockaddr_in cin);
//退出函数
int do_quit(int sfd,cli_linklist *head,struct infor rcv_info,struct sockaddr_in cin);
int do_send(int sfd, struct sockaddr_in sin);
//创建节点
cli_linklist *create_node()
{cli_linklist * p=(cli_linklist *)malloc(sizeof(cli_linklist));if(NULL==p)return NULL;p->next=NULL;return p;
}
//尾插
//客户端链表尾插
cli_linklist *insert_rear(cli_linklist *head,struct sockaddr_in cin)
{cli_linklist * s=create_node();if(NULL==s)return head;s->cin=cin;if(NULL==head){head=s;return s;}else{cli_linklist * p=head;while(p->next!=NULL)p=p->next;p->next=s;return head;}
}//删除
//链表中删除该地址信息
//段错误删链表问题
cli_linklist *exit_chat(cli_linklist *head,struct sockaddr_in cin)
{if(head->next==NULL)//只有一个客户端时{free(head);head=NULL;return head;}cli_linklist *p=head;while(p->next!=NULL)  //两个以上客户端{if(memcmp(&(p->next->cin),&cin,sizeof(cin))==0)//找到p下一个节点地址信息符合的{cli_linklist *del=p->next;p->next=del->next;free(del);del=NULL;break;}else{p=p->next;}}return head;
}int main(int argc, const char *argv[])
{//if(argc<3){printf("请输入ip号和端口号\n");return -1;}//创建套接字int sfd=socket(AF_INET,SOCK_DGRAM,0);//SOCK_DGRAM:表示使用UDP套接字通信if(sfd==-1){perror("socket error");return -1;}//2绑定//2.1填充地址信息结构体struct sockaddr_in sin;sin.sin_family =AF_INET;sin.sin_port=htons(atoi(argv[2])); //端口号(argv[0])sin.sin_addr.s_addr=inet_addr(argv[1]); //ip地址(argv[1])//绑定工作if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1){perror("bind error");return -1;}/*//定义变量存储客户端地址信息结构体struct sockaddr_in cin;socklen_t socklen=sizeof(cin);*///创建进程pid_t pid=0; //存放子进程号pid=fork(); // 创建子进程if(pid>0){//父进程:用于接收连接客户端的请求//申请空间创建节点//cli_linklist *head=(cli_linklist *)malloc(sizeof(cli_linklist)); //申请空间//数据区域和指针区域初始化//head->cin=NULL;//head->next=NULL;//父进程接收客户端信息函数do_recv(sfd);}else if(pid==0) //子进程发送消息{//子进程发送消息给客户端函数do_send(sfd,sin);}//关闭套接字close(sfd);return 0;
}//父进程接收客户端信息函数
int do_recv(int sfd)
{struct infor rcv_info; //从客户端发送的信息int recvlen=0; //定义一个接收客户端发来的信息的返回值e//定义从客户端发来信息的结构体 struct sockaddr_in cin;socklen_t socklen=sizeof(cin);cli_linklist *head=NULL;//循环接收信息while(1){recvlen=recvfrom(sfd,&rcv_info,sizeof(rcv_info),0,(struct sockaddr*)&cin,&socklen);if(recvlen==-1){perror("从客户端读取信息失败了:");return -1;}printf("读取成功了,开始找程序中的从客户端发来的自己定义的协议\n");//开始找协议//先把网络字节序转换为主机字节序int type=ntohl(rcv_info.type_num);printf("type=%d\n",type);switch(type){case  login://协议1为登录{	head=insert_rear(head,cin);do_login(sfd, head,rcv_info,cin);break;}case exchange ://协议2为交流协议{do_exchange(sfd,head,rcv_info,cin);break;}case quit://为退出协议{do_quit(sfd,head,rcv_info,cin);head=exit_chat(head,cin);break;}}}
}
//登录函数
//套接子,链表,客户端信息
int do_login(int sfd, cli_linklist *head,struct infor rcv_info,struct sockaddr_in cin)
{//服务器输出(内容了)printf("%s [%s:%d] 登录成功了\n",rcv_info.name,(char *)inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));//循环遍历链表向每一个客户端发登录成功//重新拼接群聊消息: 名字+消息char rbuf[258] = "";snprintf(rbuf,sizeof(rbuf),"%s登录\n",rcv_info.name);strcpy(rcv_info.text, rbuf);//先判断链表是否为空cli_linklist *p=head;		while(p->next!=NULL)//尾插法所以最后一个不需要发{if(sendto(sfd,&rcv_info,sizeof(rcv_info),0,\(struct sockaddr*)&(p->cin),sizeof(p->cin))==-1){perror("writer error登录写入错误");return -1;}//链表向后移动			p=p->next;}return 0;
}
//发送函数
int do_exchange(int sfd,cli_linklist *head,struct infor rcv_info,struct sockaddr_in cin)
{//服务器显示发送成功printf("%s [%s:%d] 发送成功了\n",rcv_info.name,(char *)inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));//重新拼接群聊消息: 名字+消息char rbuf[258] = "";snprintf(rbuf,sizeof(rbuf),"%s:%s",rcv_info.name,rcv_info.text);strcpy(rcv_info.text, rbuf);//循环遍历除自己以外的客户端,分别发送信息cli_linklist *p=head;while(p!=NULL){if(p->cin.sin_addr.s_addr==cin.sin_addr.s_addr&&p->cin.sin_port==cin.sin_port){	p=p->next;continue;}//向除了自己以外的客户端发送消息if(sendto(sfd,&rcv_info,sizeof(rcv_info),0,\(struct sockaddr*)&(p->cin),sizeof(p->cin))==-1){perror("writer error交流写入错误");return -1;}p=p->next;}return 0;
}
//退出函数
int do_quit(int sfd,cli_linklist *head,struct infor rcv_info,struct sockaddr_in cin)
{//服务器显示退出成功的信息printf("%s [%s:%d] 退出成功了\n",rcv_info.name,\(char *)inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));//循环向各个客户端发送退出成功的信息sprintf(rcv_info.text, "-----%s 已下线-----\n", rcv_info.name);cli_linklist *p=head;while(p!=NULL){if(p->cin.sin_addr.s_addr==cin.sin_addr.s_addr&&p->cin.sin_port==cin.sin_port){	p=p->next;continue;}//向除了自己以外的客户端发送消息if(sendto(sfd,&rcv_info,sizeof(rcv_info),0,\(struct sockaddr*)&(p->cin),sizeof(p->cin))==-1){perror("writer error交流写入错误");return -1;}p=p->next;}return 0;
}/*int do_send(int sfd, struct sockaddr_in sin)
{//自己封装协议struct infor types;types.type_num=htonl(exchange);  //2发送协议while(1){//清零bzero(types.text,128);//从终端写入数据fgets(types.text,sizeof(types),stdin);types.text[strlen(types.text)-1] = 0;//将当前进程当做客户端,父进程当做服务器,发送信息;if(sendto(sfd, &types, sizeof(types), 0, (void*)&sin, sizeof(sin)) < 0){perror("系统消息错误:");return -1;}}printf("系统消息发送成功");return 0;
}*/
int do_send(int sfd, struct sockaddr_in sin)
{type_s sys_msg;sys_msg.type_num=htonl(2);strcpy(sys_msg.name,"system");while(1){bzero(sys_msg.text, 128);fgets(sys_msg.text, 128, stdin);sys_msg.text[strlen(sys_msg.text)-1] = 0;//将当前进程当做客户端,父进程当做服务器,发送信息;if(sendto(sfd, &sys_msg, sizeof(sys_msg), 0, (void*)&sin, sizeof(sin)) < 0){//ERR_LOG("sendto");return -1;}}printf("系统消息发送成功");return 0;
}

客户端:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <a.h>
#define login 1 //登录协议
#define exchange 2 //交流协议
#define QUIT 3 //退出协议
//定信息的结构体体内容
typedef struct infor
{int type_num; //协议char name[50]; //名字char text[128]; //发送的内容
}type_s;
void handler(int sig)
{//回收子进程资源并退出while(waitpid(-1,NULL, WNOHANG)>0);exit(0);
}
int do_recv(int sfd);
int do_chat(int sfd, type_s climsg, struct sockaddr_in sin);
int main(int argc, const char *argv[])
{if(argc < 3){   printf("请输入 ip 和端口号\n");return -1; }//注册信号处理函数if(signal(SIGCHLD,handler)==SIG_ERR){perror("signal error");return -1;}//创建套接字int sfd=socket(AF_INET,SOCK_DGRAM,0);//SOCK_DGRAM:表示使用UDP套接字通信if(sfd==-1){perror("socket error");return -1;}//2.1填充地址信息结构体struct sockaddr_in sin;sin.sin_family =AF_INET;sin.sin_port=htons(atoi(argv[2])); //端口号(argv[0])sin.sin_addr.s_addr=inet_addr(argv[1]); //ip地址(argv[1])//封装登录协议type_s climsg;climsg.type_num=htonl(1);printf("请输入姓名>>>");fgets(climsg.name,128,stdin);climsg.name[strlen(climsg.name)-1]=0;//向服务器发送登录协议if(sendto(sfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){perror("sendtio error");return -1;}//定义线程pid_t pid = fork();if(pid > 0){//父进程获取信息do_recv(sfd);}else if(pid==0){//子进程发送信息do_chat(sfd, climsg, sin);}//4.关闭套接字close(sfd);		return 0;
}
int do_chat(int sfd, type_s climsg, struct sockaddr_in sin)
{while(1){		//从终端获取聊天信息bzero(climsg.text, sizeof(climsg.text));fgets(climsg.text, 128 ,stdin);climsg.text[strlen(climsg.text)-1] = 0;if(strcmp(climsg.text,"quit")==0){climsg.type_num=htonl(QUIT);}else{climsg.type_num=htonl(exchange);}//发送给服务器if(sendto(sfd, &climsg, sizeof(climsg), 0, (struct sockaddr*)&sin, sizeof(sin)) < 0){perror("发送错误");return -1;}//退出进程if(strcmp(climsg.text,"quit")==0){exit(0);}}
}
int do_recv(int sfd)
{struct infor rcv_info; // 从服务端发送的信息int recvlen = 0; // 定义一个接收服务端发来的信息的返回值struct sockaddr_in server_addr; // 服务器地址信息socklen_t addrlen = sizeof(server_addr); // 地址信息长度while (1) {recvlen = recvfrom(sfd, &rcv_info, sizeof(rcv_info), 0, (struct sockaddr*)&server_addr, &addrlen);if (recvlen == -1) {perror("从客户端读取信息失败了:");return -1;}printf("%s\n", rcv_info.text);}
}

运行效果:

应该还是有不少Bug的我记得有个段错误,但是没修改然后就莫名其妙的好了,后面还会在修改的,本代码参考chatgpt的解答

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

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

相关文章

《WebKit 技术内幕》之七(4): 渲染基础

4 WebKit软件渲染技术 4.1 软件渲染过程 在很多情况下&#xff0c;也就是没有那些需要硬件加速内容的时候&#xff08;包括但不限于CSS3 3D变形、CSS3 03D变换、WebGL和视频&#xff09;&#xff0c;WebKit可以使用软件渲染技术来完成页面的绘制工作&#xff08;除非读者强行…

【Redis】Redis如何实现key的过期删除

​ &#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Redis ⛳️ 功不唐捐&#xff0c;玉汝于成 ​ 目录 前言 正文 结语 我的其他博客 前言 在当今信息时代&#xff0c;数据的快速存储和高效检索成为了软件系统设计中的核心需求。Redis作为…

为什么PostgreSQL如此受追捧?

今天在群里看到一张图&#xff0c; 题目叫为什么PostgreSQL如此受追捧&#xff1f;列举了PG的7大特性&#xff0c;感觉很有意思&#xff0c;实际上在中国PG并没有Mysql流行&#xff0c;但是mysql被oracle收购之后&#xff0c;大家对mysql的发展前景不免有些担心&#xff0c;但是…

golang学习笔记——http.Handle和http.HandleFunc的区别与type func巧妙运用

文章目录 http.Handle和http.HandleFunc的区别http.Handle分析type func巧妙运用 http.HandleFunc分析总结参考资料 http.Handle和http.HandleFunc的区别 http.Handle和http.HandleFunc的区别体现了Go语言接口的巧妙运用 下面代码启动了一个 http 服务器&#xff0c;监听 808…

系统引导器GRUB

全称为GNU GRUB&#xff0c;来自GNU计划的多操作系统引导器。 作用&#xff1a; 查看引导分区内容 ls /boot ls /boot/grub2/ 查看GRUB2的配置文件 cat /boot/grub2/grub.cfg 可以看到/boot中保存的文件主要是linux内核、内存映像文件等。 注意到是子用户&#xff0c;但在…

【RabbitMQ】RabbitMQ面试热点

使用RabbitMQ有什么好处&#xff1f; 异步处理 解耦 流量削峰 RabbitMQ 结构&#xff08;如何发送消息&#xff1f;&#xff09; 整体结构如下图所示&#xff1a; 结构介绍 Server&#xff1a;又称为broker&#xff0c;接受客户端连接&#xff0c;RabbitMQ 节点&#xff1b…

华南理工大学数字信号处理实验实验二源码(薛y老师)

一、实验目的 ▪ 综合运用数字信号处理的理论知识进行信号分析并利用MATLAB作为编程工具进行计算机实现&#xff0c;从而加 深对所学知识的理解&#xff0c;建立概念。 ▪ 掌握数字信号处理的基本概念、基本理论和基本方法。 ▪ 学会用MATLAB对信号进行分析和处理。 ▪ 用F…

postman测试文件上传接口设置说明

Postman介绍及下载链接地址 Download Postman | Get Started for Free 打开postman 选择POST方法&#xff0c;然后设置goform 设置Header参数 设置Body参数&#xff0c;选择数据form-data 添加文件&#xff0c; 选择为文件属性 添加需要上传的文件

利用STM32CubeMX和keil模拟器,3天入门FreeRTOS(1.1) —— 创建多个静态任务实操和简单讲解

前言 &#xff08;1&#xff09;FreeRTOS是我一天过完的&#xff0c;由此回忆并且记录一下。个人认为&#xff0c;如果只是入门&#xff0c;利用STM32CubeMX是一个非常好的选择。学习完本系列课程之后&#xff0c;再去学习网上的一些其他课程也许会简单很多。 &#xff08;2&am…

Ranger概述及安装配置

一、前序 希望拥有一个框架,可以管理大多数框架的授权,包括: hdfs的目录读写权限各种大数据框架中的标的权限,列级(字段)权限,甚至行级权限,函数权限(UDF)等相关资源的权限是否能帮忙做书库脱敏Ranger框架应运而生。 二、Ranger 2.1、什么是ranger Apache Ranger…

re:从0开始的HTML学习之路 11. 音视频标签

1. 音视频标签 向页面中引入音频/视频。二者使用方式一样 2. 二者常用属性 controls&#xff1a;是否允许用户控制播放&#xff0c;不加则不允许 autoplay&#xff1a;控制是否自动播放 loop&#xff1a;控制是否循环播放 注意&#xff1a; HTML5中若属性名与属性值相同&…

mini-Spring-BeanDefinition和BeanDefinitionRegistry(二)

Bean工厂 首先我们需要定义 BeanFactory 这样一个 Bean 工厂&#xff0c;提供 Bean 的获取方法 getBean(String name)&#xff0c;之后这个 Bean 工厂接口由抽象类 AbstractBeanFactory 实现&#xff0c;可以统一模板。继承抽象类 AbstractBeanFactory 后的 AbstractAutowireCa…