当前位置: 首页 > news >正文

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的可靠性保证机制。

相关文章:

Linux 基本语句_16_Udp网络聊天室

代码&#xff1a; 服务端代码&#xff1a; #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…...

使用ffmpeg命令进行视频格式转换

1 ffmpeg介绍 FFmpeg 是一个非常强大和灵活的开源工具集&#xff0c;用于处理音频和视频文件。它提供了一系列的工具和库&#xff0c;可以用于录制、转换、流式传输和播放音频和视频。 FFmpeg 主要特点如下&#xff1a; 格式支持广泛&#xff1a;FFmpeg 支持几乎所有的音频和视…...

Mac安装Adobe AE/pr/LR/ai/ps/au/dw/id 2024/2023报错问题解决(常见错误:已损坏/2700/146/130/127)

1.打开允许“允许任何来源” 如何打开允许任何来源&#xff1f;在 Finder 菜单栏选择 【前往】 – 【实用工具 】&#xff0c;找到【终端】程序&#xff0c;双击打开&#xff0c;在终端窗口中输入&#xff1a;sudo spctl --master-disable 输入代码后&#xff0c;按【return …...

Python三级 每周练习题31

如果你感觉有收获&#xff0c;欢迎给我微信扫打赏码 ———— 以激励我输出更多优质内容 练习一: 作业1:编写程序&#xff0c;在下面的字典中找出身高137的同学并输出姓名&#xff0c;如果没找到&#xff0c; 输出没有 a{‘小赵’:136,‘小钱’:141,‘小孙’:146,‘小李’:13…...

【DataSophon】大数据服务组件之Flink升级

&#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341;&#x1fa81;&#x1f341; &#x1fa81;&#x1f341;&#x1fa81;&am…...

Android笔记(十八):面向Compose组件结合Retrofit2和Rxjava3实现网络访问

一、Retrofit2 Square公司推出的Retrofit2库&#xff08;https://square.github.io/retrofit/&#xff09;&#xff0c;改变了网络访问的方式。它实现了网络请求的封装。Retrofit库采用回调处理方式&#xff0c;使得通过接口提交请求和相应的参数的配置&#xff0c;就可以获得…...

mybatis中oracle的sql没走索引导致特别慢(未加jdbcType的)

如果直接跑sql是能走索引很快&#xff0c;在mybatis中不能&#xff0c;可能就是jdbcType的原因。 比如&#xff0c;我有一个属性A&#xff0c;在表里面是VARCHAR2类型&#xff0c;但是在mybatis中的sql是#{a}&#xff0c;缺少jdbcTypeJdbcType.VARCHAR&#xff0c;就会导致myba…...

QT自带打包问题:无法定位程序输入点?metaobject@qsound

文章目录 无法定位程序输入点?metaobjectqsound……检查系统环境变量的配置&#xff1a;打包无须安装qt的文件 无法定位程序输入点?metaobjectqsound…… 在执行release打包程序后&#xff0c;相应的release文件夹下的exe文件&#xff0c;无法打开 如有错误欢迎指出 检查系…...

7.3 lambda函数

一、语法 1.基础语法 [capture](paramLists) mutable ->retunType{statement} capture。捕获列表&#xff0c;用于捕获前文的变量供lambda函数中使用&#xff0c;可省略。(paramLists)。参数列表&#xff0c;可省略。mutable。lambda表达式默认具有常量性&#xff0c;可以…...

dcoker-compose一键部署EFAK —— 筑梦之路

简介 EFAK&#xff08;Eagle For Apache Kafka&#xff0c;以前称为 Kafka Eagle&#xff09;是一款由国内公司开源的Kafka集群监控系统&#xff0c;可以用来监视kafka集群的broker状态、Topic信息、IO、内存、consumer线程、偏移量等信息&#xff0c;并进行可视化图表展示。独…...

音视频:Ubuntu下安装 FFmpeg 5.0.X

1.安装相关依赖 首可选一&#xff1a; sudo apt-get update sudo apt-get install build-essential autoconf automake libtool pkg-config \libavcodec-dev libavformat-dev libavutil-dev \libswscale-dev libresample-dev libavdevice-dev \libopus-dev libvpx-dev libx2…...

【LSM tree 】Log-structured merge-tree 一种分层、有序、面向磁盘的数据结构

文章目录 前言基本原理读写流程写流程读流程 写放大、读放大和空间放大优化 前言 LSM Tree 全称是Log-structured merge-tree, 是一种分层&#xff0c;有序&#xff0c;面向磁盘的数据结构。其核心原理是磁盘批量顺序写比随机写性能高很多&#xff0c;可以通过围绕这一原理进行…...

配置OSPF与BFD联动示例

定义 双向转发检测BFD&#xff08;Bidirectional Forwarding Detection&#xff09;是一种用于检测转发引擎之间通信故障的检测机制。 BFD对两个系统间的、同一路径上的同一种数据协议的连通性进行检测&#xff0c;这条路径可以是物理链路或逻辑链路&#xff0c;包括隧道。 …...

01到底应该怎么理解“平均负载”

1、如何了解系统的负载情况&#xff1f; 每次发现系统变慢时&#xff0c; 我们通常做的第⼀件事&#xff0c; 就是执⾏top或者uptime命令&#xff0c; 来了解系统的负载情况。 ⽐如像下⾯这样&#xff0c; 我在命令⾏⾥输⼊了uptime命令&#xff0c; 系统也随即给出了结果。 …...

jmeter,动态参数之随机数、随机日期

通过函数助手&#xff0c;执行以下配置&#xff1a; 执行后的结果树&#xff1a; 数据库中也成功添加了数据&#xff0c;对应字段是随机值&#xff1a;...

uniApp常见知识点-问题答案

1、uniApp中如何进行页面跳转&#xff1f; 答案&#xff1a;可以使用 uni.navigateTo、uni.redirectTo 和 uni.reLaunch 等方法进行页面跳转。其中&#xff0c;uni.navigateTo可以实现页面的普通跳转&#xff0c; uni.redirectTo可以实现页面的重定向跳转&#xff0c; uni.reL…...

云原生基础入门概念

文章目录 发现宝藏云原生的概念云原生的关键技术为何选择云原生&#xff1f;云原生的实际应用好书推荐 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【宝藏入口】。 云原生的概念 当谈及现…...

一个 tomcat 下如何部署多个项目?附详细步骤

一个tomcat下如何部署多个项目&#xff1f;Linux跟windows系统下的步骤都差不多&#xff0c;以下linux系统下部署为例。windows系统下部署同理。 1 不修改端口&#xff0c;部署多个项目 清楚tomcat目录结构的应该都知道&#xff0c;项目包是放在webapps目录下的&#xff0c;那…...

pycharm强制让terminal停止执行的快捷键

CtrlC即可...

MFC(Microsoft Foundation Classes)中 MessageBox

在MFC&#xff08;Microsoft Foundation Classes&#xff09;中&#xff0c;MessageBox是一个常用的对话框类&#xff0c;用于显示消息框并与用户进行交互。MessageBox类提供了多种用法和选项&#xff0c;以下是一些常见的用法和示例说明&#xff1a; 显示简单的消息框&#x…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...