Linux 基本语句_16_Udp网络聊天室

代码:

服务端代码:

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>#define N 128
#define L 1
#define C 2
#define Q 3typedef struct{int type;char name[N];char text[N];
}MSG; // 存信息 typedef struct node{struct sockaddr_in addr; // 存ip 和 端口号 struct node *next; // 链表 
}linklist_t;linklist_t *linklist_create(); // 创建链表函数 void do_login(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr); // 某客端上线,将数据发送给其他在线客户端 
void do_chat(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr); // 将用户想要发送的数据广播给其他用户 
void do_quit(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr); // 在链表中删除自己的记录,并将自己退出的信息发送給其他客户端 int main(int argc, const char *argv[]){int sockfd;struct sockaddr_in serveraddr, clientaddr; // 储存信息 socklen_t addrlen = sizeof(serveraddr);if(argc < 3){printf("argc number error\n");return -1;}/* 创建套接字 */ if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ // IPV4、UDP协议、协议标志 printf("socket error\n");return -1;}/* 填充服务器网络信息 */ serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]);serveraddr.sin_port = htons(atoi(argv[2]));if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0){ // 套接字与服务器网络信息绑定、(套接字是中转站,bind将ip和端口信息存入中转站)printf("bind error\n");return -1;}MSG msg;pid_t pid;if((pid = fork()) < 0){printf("fork error\n");return -1;}else if(pid == 0){ // 子进程 msg.type = C;strcpy(msg.name, "server");while(1){fgets(msg.text, N, stdin); // 等待控制台输入msg.text[strlen(msg.text) - 1] = '\0';sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, addrlen); // 子进程和父进程绑定在同一个ip地址 和 端口号 子进程能向父进程发送给对方 } }else{ // 父进程负责接收数据并处理 linklist_t *h = linklist_create();while(1){recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &addrlen); // 从中转站接收数据直到有数据为止 printf("%d -- %s -- %s\n", msg.type, msg.name, msg.text); // 打印接收的数据 switch(msg.type){ // 根据数据的类型做不同操作 case L:do_login(msg, h, sockfd, clientaddr); // 登录广播提醒 break;case C:do_chat(msg, h, sockfd, clientaddr); // 广播聊天 break;case Q:do_quit(msg, h, sockfd, clientaddr); // 广播退出 break;}}   }return 0; 
}linklist_t *linklist_create(){ // 创建链表 linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t)); // 创建一个链表节点,h为链表头部 h->next = NULL; // 整个链表只有一个节点 return h; 
}void do_login(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr){linklist_t *p = h;/* 用户登录信息发送给其他客户 */ sprintf(msg.text, "-------- %s login -------------", msg.name);while(p->next != NULL){sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->next->addr, sizeof(struct sockaddr_in)); // 发送那个给链表中所有对象 p = p->next;}linklist_t *temp = (linklist_t *) malloc(sizeof(linklist_t)); // 创建一个新结点 temp->addr = clientaddr; // 客户端信息存入结点 temp->next = h->next;h->next = temp; // 将temp存入链表末尾 return;
}void do_chat(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr){char buf[N] = {};linklist_t *p = h;/* 将用户信息发送给其他在线的用户 */sprintf(buf, "%s : %s", msg.name, msg.text);strcpy(msg.text, buf); // 将数据存入msg while(p->next != NULL){ // 发送数据 if(memcmp(&clientaddr, &p->next->addr, sizeof(clientaddr)) == 0){ // 数据是自己的就不传输了 p = p->next;} else{ // 其他人就发送 sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->next->addr, sizeof(struct sockaddr_in)); // 发送的数据、长度、发送的位置 p = p->next;}}return; 
}void do_quit(MSG msg, linklist_t *h, int sockfd, struct sockaddr_in clientaddr){linklist_t *p = h;linklist_t *temp;/* 将用户退出的信息发送给其他用户,并将其信息从链表中删除 */sprintf(msg.text, "-------- %s offline --------", msg.name);while(p->next != NULL){if(memcmp(&clientaddr, &p->next->addr, sizeof(clientaddr)) == 0){ // 自己的就不发送 temp = p->next;p->next = temp->next;free(temp); // 释放本结点 temp = NULL; // 指针至为空指针 }else{sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->next->addr, sizeof(struct sockaddr_in)); // 不是自己就发送 p = p->next;}}return; 
}

客户端代码:

#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <strings.h>#define N 128
#define L 1
#define C 2
#define Q 3typedef struct{int type;char name[N];char text[N];
}MSG; // 存信息 int main(int argc, const char *argv[]){int sockfd;struct sockaddr_in serveraddr, clientaddr; // 储存信息 socklen_t addrlen = sizeof(serveraddr);if(argc < 3){printf("argc number error\n");return -1;}/* 创建套接字 */ if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ // IPV4、UDP协议、协议标志 printf("socket error\n");return -1;}/* 填充服务器网络信息 */ serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = inet_addr(argv[1]);serveraddr.sin_port = htons(atoi(argv[2]));MSG msg;msg.type = L;printf("please enter your name: ");fgets(msg.name, N, stdin); // 将控制台输入的信息传入name中msg.name[strlen(msg.name) - 1] = '\0'; sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, addrlen); // 将数据发送给服务端 pid_t pid;if((pid = fork()) < 0){printf("fork error\n");return -1;}else if(pid == 0){ // 子进程,发送数据 while(1){fgets(msg.text, N, stdin); // 等待控制台输入msg.text[strlen(msg.text) - 1] = '\0';if(strncmp(msg.text, "quit", 4) == 0){ // 若要退出 msg.type = Q; // 退出广播 sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, addrlen); // 子进程和父进程绑定在同一个ip地址 和 端口号 子进程能向父进程发送给对方close(sockfd);kill(getppid(), SIGKILL); // 退出父进程 return 0; 	} msg.type = C; // 聊天 sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, addrlen); // 子进程和父进程绑定在同一个ip地址 和 端口号 子进程能向父进程发送给对方} }else{ // 父进程负责接收数据while(1){recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, &addrlen); // 从中转站接收数据直到有数据为止 printf("%s\n", msg.text); // 打印接收的数据 }   }return 0; 
}

效果:

在这里插入图片描述
总体效果是客户上线状态、退出状态、发送的消息都能通过广播,将信息发送给所有在线客户端,服务端能接收并显示所有客户端发送的消息,且也具备广播能力

原理:

服务端:

服务端创建了一个链表,这个链表中的每个节点是专用于储存客户端的ip地址和端口号等一系列信息,目的是方便遍历实现广播功能

服务端的创建了父子进程,子进程专门接收控制台发送的数据,并转发给父进程,父进程将数据广播给所有客户端,朴素的讲子进程接收控制台数据,父进程接收客户端信息并广播

客户端:

客户端也是创建父子进程,父进程负责接收服务器转发的数据,并打印。子进程负责发送,从本控制台获取的信息并发送给服务端通过服务器广播给其他客户端

服务器本质就是中转站,负责接收客户端信息状态并广播

拓展:

套接字:

套接字可以理解为网络通信的中转站,将通信双方的ip地址和端口号等相关信息存入套接字,以便通信双方能通过ip地址和端口号找到对应接收端。

服务端和客户端都创建套接字的原因:

在一个典型的客户端-服务器模型中,服务器和客户端通过套接字建立通信。一般情况下,服务器会先创建一个套接字并绑定到一个特定的 IP 地址和端口上,然后等待客户端连接。
在客户端与服务器建立连接时,客户端会创建一个新的套接字,并尝试连接到服务器的套接字地址。如果连接成功,服务器会接受这个连接并为客户端创建一个新的套接字,该套接字将用于与这个特定客户端之间的通信。
这样,服务器会保持一个主套接字用于监听客户端的连接请求,并为每个连接创建一个新的套接字来处理与特定客户端之间的通信。这些连接的套接字通常是独立的,即服务器和每个客户端之间都有一个独立的套接字,用于他们之间的通信。

UDP连接方式:

在 UDP 协议中,客户端并不需要显式地调用 bind() 来绑定一个端口。通常情况下,在客户端发送数据时,系统会自动分配一个临时的端口号,并在发送数据时使用这个端口号。这个临时端口号通常在发送后被释放,因此客户端不需要显式地绑定一个端口。
客户端在发送数据时,使用 sendto() 或者 sendmsg() 等函数向目标服务器发送数据报。在发送时,指定目标服务器的 IP 地址和端口号即可,而不需要调用 bind() 来指定客户端的本地端口。UDP是无连接的,因此客户端不需要事先建立连接,只需要在发送数据时指定目标地址和端口即可。
相反,服务器通常会先调用 bind() 来绑定一个固定的端口号,以便监听客户端发送来的数据。服务器需要绑定一个固定端口号来等待客户端的连接请求或者接收数据报。
总之,在UDP中,客户端通常不需要显式地调用 bind() 来绑定端口,它可以自动分配一个临时的端口来发送数据。服务器端则需要绑定一个固定的端口号来等待客户端的连接或接收数据。

区别:

TCP是面向连接的协议,它在通信之前需要建立连接,并确保数据传输的可靠性。它提供数据的可靠性保证、流量控制和拥塞控制。
UDP是无连接的协议,不需要在发送数据之前建立连接。它不保证数据的可靠性,也不提供类似TCP的可靠性保证机制。

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

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

相关文章

中小型企业网络综合实战案例分享

实验背景 某公司总部在厦门&#xff0c;北京、上海都有分部&#xff0c;网络结构如图所示&#xff1a; 一、网络连接描述&#xff1a; 厦门总部&#xff1a;内部网络使用SW1、SW2、SW3三台交换机&#xff0c;SW1为作为核心交换机&#xff0c;SW2、SW3作为接入层交换机&#x…

PDF控件Spire.PDF for .NET【转换】演示:将多个图像转换为单个 PDF

如果您想要将多个图像合并为一个文件以便于分发或存储&#xff0c;将它们转换为单个 PDF 文档是一个很好的解决方案。这一过程不仅节省空间&#xff0c;还确保所有图像都保存在一个文件中&#xff0c;方便共享或传输。在本文中&#xff0c;您将学习如何使用Spire.PDF for .NET …

-bash: docker-compose: 未找到命令

-bash: docker-compose: 未找到命令 我在使用Docker搭建Nacos容器时遇到了这个问题&#xff1a;是没有安装 docker-compose工具 。 docker-compose的用处主要体现在以下几个方面&#xff1a; 快速搭建开发环境&#xff1a;使用docker-compose可以快速搭建起开发环境&#xff0…

一分钟轻松制作AI数字人播报视频

随着人工智能的快速发展&#xff0c;AI数字人播报成为了媒体和信息传播领域的一项创新技术。AI数字人播报是利用人工智能技术创建的一系列短视频&#xff0c;以新闻主播为中心&#xff0c;展示各种场景和情境能够以短视频的形式进行新闻的报道。这种创新的内容形式在社交媒体和…

Java刷题篇——单链表练习题上

206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 1. 题目描述 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例1 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例2 输入&#xff1a;head [1,2] 输出&…

Spring Boot 3 + Vue 3 整合 WebSocket (STOMP协议) 实现广播和点对点实时消息

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

高可用接入层技术演化及集群概述

集群概述 集群的介绍及优势 集群&#xff1a;将多台服务器通过硬件或软件的方式组合起来&#xff0c;完成特定的任务&#xff0c;而这些服务器对外表现为一个整体。集群的优势 高可靠性&#xff1a;利用集群管理软件&#xff0c;当主服务器故障时&#xff0c;备份服务器能够自…

2023年国赛高教杯数学建模D题圈养湖羊的空间利用率解题全过程文档及程序

2023年国赛高教杯数学建模 D题 圈养湖羊的空间利用率 原题再现 规模化的圈养养殖场通常根据牲畜的性别和生长阶段分群饲养&#xff0c;适应不同种类、不同阶段的牲畜对空间的不同要求&#xff0c;以保障牲畜安全和健康&#xff1b;与此同时&#xff0c;也要尽量减少空间闲置所…

代码随想录算法训练营第五十五天 _ 动态规划_392. 判断子序列、115.不同的子序列。

学习目标&#xff1a; 动态规划五部曲&#xff1a; ① 确定dp[i]的含义 ② 求递推公式 ③ dp数组如何初始化 ④ 确定遍历顺序 ⑤ 打印递归数组 ---- 调试 引用自代码随想录&#xff01; 60天训练营打卡计划&#xff01; 学习内容&#xff1a; 392. 判断子序列 这个题目就是 …

消息队列kafka详解:Kafka原理分析总结

一、概述 Kakfa起初是由LinkedIn公司开发的一个分布式的消息系统&#xff0c;后成为Apache的一部分&#xff0c;它使用Scala编写&#xff0c;以可水平扩展和高吞吐率而被广泛使用。目前越来越多的开源分布式处理系统如Cloudera、Apache Storm、Spark等都支持与Kafka集成。 Kaf…

SGPIO介紹

什么是SGPIO&#xff1f; Serial General Purpose Input Output (SGPIO) is a method to serialize general purpose IO signals. SGPIO defines the communication between an initiator (e.g. a host bus adapter) and a target (e.g. a backplaneholding disk drives). The …

最小窗口子串算法题——Java解答

题目&#xff1a;最小窗口子串 题目描述&#xff1a; 给你一个字符串 s 和一个字符串 t&#xff0c;返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空字符串 ""。 注意&#xff1a; 对于 t 中重复的字符&#xff…

Axure的交互与情形,事件,动作

交互样式 交互样式是指当用户与原型进行交互时&#xff0c;元素所呈现出的视觉效果。在Axure中&#xff0c;可以通过设置交互样式来调整元素在交互过程中的外观&#xff0c;例如改变颜色、大小、位置等。 交互事件 交互事件是指在用户与原型进行交互时触发的动作。在Axure中&…

一次应急响应记录

背景&#xff1a; 周五晚上&#xff0c;我健身完回到宿舍收到qq消息&#xff0c;原来是安全厂商在扫描资产时&#xff0c;发现一批openssh漏洞如下图&#xff1a; 其实我是一名小白&#xff0c;我的第一反应就是升级openssh版本。但是这里问题又来了&#xff0c;我们内网主机是…

Python 自动化之收发邮件(一)

imapclient / smtplib 收发邮件 文章目录 imapclient / smtplib 收发邮件前言一、基本内容二、发送邮件1.整体代码 三、获取邮件1.整体代码 总结 前言 简单给大家写个如何用Python进行发邮件和查看邮件教程&#xff0c;希望对各位有所帮助。 一、基本内容 本文主要分为两部分…

基于ssm在线作业管理系统的设计与实现论文

摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管理就很关键。因此学生作业信息的…

Web Worker的快速理解与简单应用

Web Worker 是一种在浏览器环境中运行的脚本它在主线程之外运行&#xff0c;可以在后台执行计算密集型或需要长时间运行的任务&#xff0c;而不会阻塞主线程的执行。Web Worker 通过在独立的线程中执行&#xff0c;可以提高页面的性能和响应性。 实现流程&#xff1a; 主线程&a…

补题与总结:牛客小白月赛83(B~F)

文章目录 写在最前面的复盘B-小天的魔法&#xff08;贪心 模拟 双指针&#xff09;C-小天的 Minecraft&#xff08;概率&#xff09;D-小天的子序列&#xff08;预处理 排列组合&#xff09;E-小天的贪吃蛇&#xff08;模拟&#xff09;F-小天的 AB&#xff08;结论题&#xff…

举个栗子!Tableau 技巧(261):自由切换图表的背景颜色

数据粉反馈了一个有趣的需求&#xff1a;Tableau 图表的背景颜色可以设置多个&#xff0c;然后按需切换么&#xff1f; 我们知道&#xff0c;图表的背景颜色对于可视化分析呈现来说非常重要。大部分情况下&#xff0c;我们会固定使用一种背景色。 如果确实需要切换图表背景颜…

K8s内容器拓扑图工具

1.背景&#xff1a;随着线上容器越来越多&#xff0c;需要一个可视化的方式展示各个容器之间的拓扑图。 2.需求&#xff1a;轻量级&#xff0c;部署方便。 3.部署 helm repo add groundcover https://helm.groundcover.com/ helm repo update helm install caretta --namespa…