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.rhkb.cn/news/219621.html

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

相关文章

云基础软件深化合作,云轴科技ZStack与麒麟软件战略签约

12月8日&#xff0c;云轴科技ZStack与麒麟软件战略合作签约仪式在北京举行&#xff0c;双方对过往紧密合作表达了充分肯定&#xff0c;并就进一步联合技术创新、打造重点行业标杆和持续赋能客户达成高度共识。云轴科技创始人&CEO张鑫和麒麟软件高级副总经理谢文征共同见证双…

教师考编需要什么条件

教师考编&#xff0c;了解考编需要什么条件是非常重要的。接下来&#xff0c;我来介绍几点教师考编的条件。 需要具备相应的学历背景。一般来说&#xff0c;考编需要具备本科或以上学历&#xff0c;并且所学专业与所报考的岗位相关。在某些特殊情况下&#xff0c;如报考幼儿园教…

sap table 获取 valuation class MBEW 查表获取

参考 https://www.tcodesearch.com/sap-tables/search?qvaluationclass

【️接口和抽象类的区别,如何选择?】

✅接口和抽象类的区别&#xff0c;如何选择&#xff1f; ✅ 接口和抽象类的区别✅方法定义✅修饰符✅构造器✅继承和实现✅单继承 、 多实现✅职责不同 ✅什么是模板方法模式&#xff0c;有哪些应用呢&#xff1f;✅典型理解✅示例&#x1f4a1;思考 ✅你在工作中是如何使用设计…

基于Java+Swingt学生信息管理系统

基于JavaSwing学生信息管理系统 一、系统介绍二、功能展示四、其他系统实现五、获取源码 一、系统介绍 1.用户登陆&#xff1a;在帮助按钮处&#xff0c;可以查看登陆账号及密码&#xff1a; 账号admin,密码123456 在未输入的情况下&#xff0c;会提示用户名不能为空&#xff…

设计模式——代理模式(结构型)

引言 代理模式是一种结构型设计模式&#xff0c; 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问&#xff0c; 并允许在将请求提交给对象前后进行一些处理。 问题 为什么要控制对于某个对象的访问呢&#xff1f; 举个例子&#xff1a; 有这样一个消耗大量…

为什么选择计算机?大数据时代学习计算机的价值探讨

还记得当初自己为什么选择计算机? 计算机是在90年代兴起的专业,那时候的年轻人有驾照、懂外语、懂计算机是很时髦的事情! 当初你问我为什么选择计算机,我笑着回答:“因为我梦想成为神奇的码农!我想像编织魔法一样编写程序,创造出炫酷的虚拟世界!”谁知道,我刚入门的…

java内置的数据结构

Java语言提供了许多内置的数据结构&#xff0c;包括&#xff1a; 1. 数组&#xff08;Array&#xff09;&#xff1a;数组是最基本的数据结构之一&#xff0c;它是一个有序的元素集合&#xff0c;每个元素都有一个对应的索引。在Java中&#xff0c;数组可以通过声明和初始化来创…

stm32F4——蜂鸣器与按键的实例使用

stm32F4——蜂鸣器与按键的实例使用 蜂鸣器和按键的实质都是GPIO的使用&#xff0c;所以基本原理就不介绍啦&#xff0c;基本寄存器其实都是GPIO的高低电平的赋值&#xff0c;可以参考stm32——LEDGPIO的详细介绍 使用的开发板是华清远见的stm32F407VET6&#xff0c;都是M4的内…

AtCoder Beginner Contest 332 G. Not Too Many Balls(最大流转最小割 dp)

题目 n(n<500)种球&#xff0c;第i种有ai(0<ai<1e12)个球&#xff0c; m(m<5e5)个盒子&#xff0c;第j个能放bj(0<bj<1e12)个球 特别地&#xff0c;第j个盒子最多能放i*j个第i种球 求m个盒子能放的最多的球的总数 思路来源 官方题解 题解 显然是一个最…

STM32——时钟树与滴答计时器

STM32——时钟树与滴答计时器 使用的开发板为stm32F407VET6的芯片,主要介绍stm32的时钟树与滴答计时器的一些理论和一个自己编写的delay函数。 时钟树的结构图可以在STM32F4xx中文参考手册.pdf中的时钟这块找到。而滴答计时器是内核资源&#xff0c;需要到Cortex M3与M4权威指南…

链路聚合 (hcia)

原理 采用链路聚合技术可以在不进行硬件升级的条件下&#xff0c;通过将多个物理接口捆绑为一个逻辑接 口&#xff0c;达到增加链路带宽的目的。在实现增大带宽目的的同时&#xff0c;链路聚合采用备份链路的机制&#xff0c; 可以有效的提高设备之间链路的可靠性 &#x…

Error message “error:0308010C:digital envelope routines::unsupported“ 解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Java_Lambda表达式JDK8新特性(方法引用)

一、Lambda表达式 接下来&#xff0c;我们学习一个JDK8新增的一种语法形式&#xff0c;叫做Lambda表达式。作用&#xff1a;用于简化匿名内部类代码的书写。 1.1 Lambda表达式基本使用 怎么去简化呢&#xff1f;Lamdba是有特有的格式的&#xff0c;按照下面的格式来编写Lamd…

认识loader和plugin

在 webpack 中&#xff0c;专注于处理 webpack 在编译过程中的某个特定的任务的功能模块&#xff0c;可以称为插件。它和 loader 有以下区别&#xff1a; 1loader 是一个转换器&#xff0c;将 A 文件进行编译成 B 文件&#xff0c;比如&#xff1a;将 A.less 转换为 A.css&…

2019年第八届数学建模国际赛小美赛A题放射性产生的热量解题全过程文档及程序

2019年第八届数学建模国际赛小美赛 A题 放射性产生的热量 原题再现&#xff1a; 假设我们把一块半衰期很长的放射性物质做成一个特定的形状。在这种材料中&#xff0c;原子核在衰变时会以随机的方向释放质子。我们假设携带质子的能量是一个常数。质子在穿过致密物质时&#x…

关于脑区的划分方法及一些模板说明

关于脑区的划分方法及一些模板说明 前言脑区划分方法的种类一些标准的脑区划分模板参考文献 前言 原创文章&#xff0c;未经同意请勿转载 Status: Completed Author: xioabai_Ry Time to Note: March 23, 2022 这里主要记录之前自己调研的有关脑区的划分方法及一些标准的脑区…

Web前端-HTML(表格与表单)

文章目录 1.表格与表单1.1 概述 2.表格 table2.1 表格概述2.2. 创建表格2.3 表格属性2.4. 表头单元格标签th2.5 表格标题caption&#xff08;了解&#xff09;2.6 合并单元格(难点)2.7 总结表格 3. 表单标签(重点)3.1 概述3.2 form表单3.3 input 控件(重点)type 属性value属性值…

机器学习算法---聚类

类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统计学检验箱…

【Redis】AOF 基础

因为 Redis AOF 的实现有些绕, 就分成 2 篇进行分析, 本篇主要是介绍一下 AOF 的一些特性和依赖的其他函数的逻辑,为下一篇 (Redis AOF 源码) 源码分析做一些铺垫。 AOF 全称: Append Only File, 是 Redis 提供了一种数据保存模式, Redis 默认不开启。 AOF 采用日志的形式来记…