网络编程_day3

 #1024程序员节

#三次握手四次挥手#四次挥手#udp#recvfrom#sendto#服务器模型#客户端模型#Linux IO模型#阻塞式IO#非阻塞IO#设置非阻塞的方式

目录

【0】复习

【1】三次握手四次挥手

四次挥手

 四次挥手既可以由客户端发起,也可以由服务器发起

【2】udp

    1. 通信流程

2. 函数接口

1.recvfrom

2.sendto

服务器模型

客户端模型

【3】Linux IO模型

1. 阻塞式IO:最常见、效率低、不耗费cpu

2. 非阻塞IO:轮询、耗费CPU,可以处理多路IO

设置非阻塞的方式

1. 通过函数自带参数设置

 2. 通过设置文件描述符的属性。把文件描述符的属性设置为非阻塞

3. 信号驱动IO/异步IO:异步通知方式,需要底层驱动的支持(了解)

问题思考


【0】复习

tcp:服务器、客户端

网络模型:OSI:应、表、会、传、网、数、物 TCP/IP:应、传、网、物

TCP:全双工、高可靠,连接

UDP:全双工、无连接、不可靠

wireshark

三次握手:SYN SYN_SEND SYN+ACK SYN_RECV ACK ESTABLISHED

【1】三次握手四次挥手

四次挥手

 四次挥手既可以由客户端发起,也可以由服务器发起

TCP连接终止需四个分节。

类比挂电话的过程:

第一次挥手:我说完了,我要挂了

第二次挥手:好的,我知道了,但是你先别急,等我把话说完

第三次挥手:好了,我说完了,咱们可以挂电话了

第四次挥手:好的,挂了吧

第一次挥手:某个应用进程首先调用close,我们称这一端执行主动关闭。这一端的TCP于是发送一个FIN分节,表示数据发送完毕。

第二次挥手:接收到FIN的另一端执行被动关闭(passive close)。这个FIN由TCP确认。它的接收也作为文件结束符传递给接收端应用进程(放在已排队等候应用进程接收到任何其他数据之后)

第三次挥手:一段时间后,接收到文件结束符的应用进程将调用close关闭它的套接口。这导致它的TCP也发送一个FIN。

第四次挥手:接收到这个FIN的原发送端TCP对它进行确认。

【2】udp

    1. 通信流程

 

服务器----------------------------------------------------------------------------》短信的接收方

  1. 创建数据报套接字(socket)------------------》有手机
  2. 指定网络信息--------------------------------------》有号码
  3. 绑定套接字(bind)------------------------------》绑定手机
  4. 接收、发送消息(recvfrom sendto)-------》收短信
  5. 关闭套接字(close)----------------------------》接收完毕

客户端---------------------------------------------------------------------------》短信的发送方

  1. 创建数据报套接字(socket)------------------》有手机
  2. 指定网络信息--------------------------------------》有对方号码
  3. 接收、发送消息(recvfrom sendto)-------》发短信
  4. 关闭套接字(close)----------------------------》发送完毕

 

2. 函数接口

1.recvfrom

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
功能:接收数据
参数:sockfd:套接字描述符buf:接收缓存区的首地址len:接收缓存区的大小flags:0src_addr:发送端的网络信息结构体的指针addrlen:发送端的网络信息结构体的大小的指针
返回值:成功接收的字节个数失败:-10:客户端退出

2.sendto

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
功能:发送数据
参数:sockfd:套接字描述符buf:发送缓存区的首地址len:发送缓存区的大小flags:0src_addr:接收端的网络信息结构体的指针addrlen:接收端的网络信息结构体的大小
返回值: 成功发送的字节个数失败:-1

服务器模型

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{char buf[128] = {0};int ret;// 1.创建数据报套接字(socket)------------------》有手机int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket err");return -1;}printf("sockfd:%d\n", sockfd);// 2.指定网络信息--------------------------------------》有号码struct sockaddr_in saddr, caddr;saddr.sin_family = AF_Isaddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = INADDR_ANY;int len = sizeof(caddr);// 3.绑定套接字(bind)------------------------------》绑定手机if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err");return -1;}printf("bind okk\n");// 4.接收、发送消息(recvfrom sendto)-------》收短信while (1){// 最后两个参数存放:发送消息的人的信息ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&caddr, &len);if (ret < 0){perror("recvfrom err");return -1;}else{printf("ip:%s port:%d buf:%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port), buf);memset(buf, 0, sizeof(buf));}}// 5.关闭套接字(close)----------------------------》接收完毕close(sockfd);return 0;
}

客户端模型

#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{char buf[128] = {0};int ret;// 1.创建数据报套接字(socket)------------------》有手机int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket err");return -1;}printf("sockfd:%d\n", sockfd);// 2.指定网络信息--------------------------------------》有号码struct sockaddr_in saddr, caddr;saddr.sin_family = AF_INET;saddr.sin_port = htons(atoi(argv[1]));saddr.sin_addr.s_addr = INADDR_ANY;int len = sizeof(caddr);// 3.绑定套接字(bind)------------------------------》绑定手机if (bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0){perror("bind err");return -1;}printf("bind okk\n");// 4.接收、发送消息(recvfrom sendto)-------》收短信while (1){// 最后两个参数存放:发送消息的人的信息ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&caddr, &len);if (ret < 0){perror("recvfrom err");return -1;}else{printf("ip:%s port:%d buf:%s\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port), buf);memset(buf, 0, sizeof(buf));}}// 5.关闭套接字(close)----------------------------》接收完毕close(sockfd);return 0;
}

注意:

1、对于TCP是先运行服务器,客户端才能运行。

2、对于UDP来说,服务器和客户端运行顺序没有先后,因为是无连接,所以服务器和客户端谁先开始,没有关系,

3、一个服务器可以同时连接多个客户端。想知道是哪个客户端登录,可以在服务器代码里面打印IP和端口号。

4、UDP,客户端当使用send的时候,上面需要加connect,这个connect不是代表连接的作用,而是指定客户端即将要发送给谁数据。这样就不需要使用sendto而用send就可以。

5、在TCP里面,也可以使用recvfrom和sendto,使用的时候将后面的两个参数都写为NULL就OK。

 

【3】Linux IO模型

4种:阻塞IO、非阻塞IO、信号驱动IO(异步IO)、IO多路复用

场景假设一

假设妈妈有一个孩子,孩子在房间里睡觉,妈妈需要及时获知孩子是否醒了,如何做?

  1. 一直看着他,一直在一个房间呆着:不累,但是不能处理其他的事情
  2. 时不时的进房间看看:累,但是可以处理其他事情
  3. 睡觉,听孩子哭不哭:互不耽误

1. 阻塞式IO:最常见、效率低、不耗费cpu

阻塞I/O 模式是最普遍使用的I/O 模式,大部分程序使用的都是阻塞模式的I/O 。

缺省情况下(及系统默认状态),套接字建立后所处于的模式就是阻塞I/O 模式。

学习的读写函数在调用过程中会发生阻塞相关函数如下:

•读操作中的read、recv、recvfrom

读阻塞--》需要读缓冲区中有数据可读,读阻塞解除

•写操作中的write、send

写阻塞--》阻塞情况比较少,主要发生在写入的缓冲区的大小小于要写入的数据量的情况下,写操作不进行任何拷贝工作,将发生阻塞,一旦缓冲区有足够的空间,内核将唤醒进程,将数据从用户缓冲区拷贝到相应的发送数据缓冲区。

注意:sendto没有写阻塞

1)无sendto函数的原因:

sendto不是阻塞函数,本身udp通信不是面向链接的,udp无发送缓冲区,即sendto没有发送缓冲区,send是有发送缓存区的,即sendto不是阻塞函数。

2)UDP不用等待确认,没有实际的发送缓冲区,所以UDP协议中不存在缓冲区满的情况,在UDP套接字上进行写操作永远不会阻塞。

•其他操作:accept、connect

 

udp丢包

 

tcp粘包

 

tcp拆包

 

TCP粘包、拆包发生原因:

发生TCP粘包或拆包有很多原因,常见的几点:

1、要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包

2、待发送数据大于MSS(传输层的最大报文长度),将进行拆包(到网络层拆包 - id ipflags )。

3、要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包

4、接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包

粘包解决办法:

解决问题的关键在于如何给每个数据包添加边界信息,常用的方法有如下:

1、发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便知道每一个数据包的实际长度了。

2、发送端将每个数据包封装为固定长度,这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。

3、可以在数据包之间设置边界,如添加特殊符号,这样,接收端通过这个边界就可以将不同的数据包拆分开。

4、延时发送

tcp粘包与udp丢包的原因 - 清风软件测试开发 - 博客园

2. 非阻塞IO:轮询、耗费CPU,可以处理多路IO

•当我们将一个套接字设置为非阻塞模式,我们相当于告诉了系统内核:“当我请求的I/O 操作不能够马上完成,你想让我的进程进行休眠等待的时候,不要这么做,请马上返回一个错误给我。”

•当一个应用程序使用了非阻塞模式的套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称做polling)。

•应用程序不停的polling 内核来检查是否I/O操作已经就绪。这将是一个极浪费CPU 资源的操作。

•这种模式使用中不普遍。

设置非阻塞的方式

1. 通过函数自带参数设置

 2. 通过设置文件描述符的属性。把文件描述符的属性设置为非阻塞

int fcntl(int fd, int cmd, ... /* arg */ );
功能:设置文件描述符属性
参数:fd:文件描述符cmd:设置方式 - 功能选择F_GETFL  获取文件描述符的状态信息     第三个参数化忽略F_SETFL  设置文件描述符的状态信息     通过第三个参数设置O_NONBLOCK  非阻塞O_ASYNC     异步O_SYNC      同步arg:设置的值  in
返回值:特殊选择返回特殊值 - F_GETFL  返回的状态值(int)其他:成功0  失败-1,更新errno使用:0为例0-原本:阻塞、读权限  修改或添加非阻塞int flags=fcntl(0,F_GETFL);//1.获取文件描述符原有的属性信息flags = flags | O_NONBLOCK;//2.修改添加权限fcntl(0,F_SETFL,flags);    //3.将修改好的权限设置回去

3. 信号驱动IO/异步IO:异步通知方式,需要底层驱动的支持(了解)

异步通知:异步通知是一种非阻塞的通知机制,发送方发送通知后不需要等待接收方的响应或确认。通知发送后,发送方可以继续执行其他操作,而无需等待接收方处理通知。

1. 通过信号方式,当内核检测到设备数据后,会主动给应用发送信号SIGIO。

2. 应用程序收到信号后做异步处理即可。

应用程序需要把自己的进程号告诉内核,并打开异步通知机制。

//1.设置将文件描述符和进程号提交给内核驱动
//一旦fd有事件响应, 则内核驱动会给进程号发送一个SIGIO的信号fcntl(fd,F_SETOWN,getpid());//2.设置异步通知int flags;flags = fcntl(fd, F_GETFL); //获取原属性flags |= O_ASYNC;       //给flags设置异步   O_ASUNC 通知fcntl(fd, F_SETFL, flags);  //修改的属性设置进去,此时fd属于异步//3.signal捕捉SIGIO信号 --- SIGIO:内核通知会进程有新的IO信号可用
//一旦内核给进程发送sigio信号,则执行handlersignal(SIGIO,handler);

 

阻塞IO(Blocking IO)

非阻塞IO(Non-blocking IO)

信号驱动IO(Signal-driven IO)

同步性

同步

非同步

异步

描述

调用IO操作的线程会被阻塞,直到操作完成

调用IO操作时,如果不能立即完成操作,会立即返回,线程可以继续执行其他操作

当IO操作可以进行时,内核会发送信号通知进程

特点

最常见、效率低、不耗费cpu,

轮询、耗费CPU,可以处理多路IO,效率高

异步通知方式,需要底层驱动的支持

适应场景

小规模IO操作,对性能要求不高

高并发网络服务器,减少线程阻塞时间

实时性要求高的应用,避免轮询开销

 

问题思考

客户端会不会知道其它客户端地址?

不知道,服务器完成,服务器存储连接的客户端信息---》链表

有几种消息类型?

登录

聊天

退出

客户端如何同时处理发送和接收?

客户端不仅需要读取服务器消息,而且需要发送消息。读取需要调用recvfrom,发送需要先调用fgets,两个都是阻塞函数。所以必须使用多任务来同时处理,可以使用多进程或者多线程来处理。

链表节点结构体:
struct node{struct sockaddr_in addr;//data   memcmpstruct node *next;
};消息对应的结构体(同一个协议)
typedef struct msg_t
{int type;//'L' C  Q    enum un{login,chat,quit};char name[32];//用户名 char text[128];//消息正文
}MSG_t;int memcmp(void *s1,void *s2,int size);//对比

程序流程图

服务器

客户端

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

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

相关文章

实验:使用Oxygen发布大型手册到Word格式

此前&#xff0c;我曾发表过一篇文章《结构化文档发布的故事和性能调优》&#xff0c;文中讨论了在将大型DITA手册转换为PDF格式时可能遇到的性能挑战及相应的优化策略。 近日&#xff0c;有朋友咨询&#xff0c;若将同样的大型手册输出为MS Word格式&#xff0c;是否也会面临…

Linux复习-C++

参考博客&#xff1a; https://blog.csdn.net/qq_45254369/article/details/126023482?ops_request_misc%257B%2522request%255Fid%2522%253A%252277629891-A0F3-4EFC-B1AC-410093596085%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&req…

[JAVAEE] 多线程的案例(一)-单例模式

目录 一. 单例模式 二. 单例模式的使用时机 三. 单例模式的关键代码 四. 单例模式的几种实现方式 4.1 饿汉方式(急) 4.2 懒汉模式(缓) a. 解决原子性的问题 b. 解决程序运行效率低下的问题 c. 解决指令重排序的问题(其次是为了解决内存可见性的问题) 五. 总结 一. …

HCIP-HarmonyOS Application Developer 习题(十七)

&#xff08;判断&#xff09;1、对于用户创建的一些临时卡片在遇到卡片服务框架死亡重启&#xff0c;此时临时卡片数据在卡片管理服务中已经删除&#xff0c;且对应的卡片ID不会通知到提供方&#xff0c;所以卡片使用方需要自己负责清理长时间未刚除的临时卡片数据。 答案&…

QT:MaintenanceTool 模块安装工具

QT的MaintenanceTool 工具对已安装的 Qt 进行卸载、修复等其他操作时提示At least one valid and enabled repository required for this action to succeed 解决方式&#xff1a;在设置中添加一个临时的仓库 https://mirrors.tuna.tsinghua.edu.cn/qt/online/qtsdkrepositor…

6,000 个网站上的假 WordPress 插件提示用户安装恶意软件

黑客使用窃取的凭证感染 WordPress 网站&#xff0c;并向其发送虚假插件&#xff0c;通过虚假的浏览器更新提示向最终用户发送恶意软件和信息窃取程序。 该恶意活动基于ClickFix假浏览器更新恶意软件的新变种&#xff0c;自 2024 年 6 月以来已使用假 WordPress 插件感染了超过…

放大器和基本运放电路的公式推导

放大器和基本运放电路的公式推导 放大器全家谱运放的渊源和数学分析基本运放电路的公式推导预备知识基本特性跟随器特性比较器特性 基本运放电路反相放大器&#xff08;反比例运算放大器&#xff09;同相放大器&#xff08;正比例运算放大器&#xff09;反相加法器同相加法器减…

Python条形图 | 指标(特征)重要性图的绘制

在数据科学和机器学习的工作流程中&#xff0c;特征选择是一个关键步骤。通过评估每个特征对模型预测能力的影响&#xff0c;我们可以选择最有意义的特征&#xff08;指标&#xff09;&#xff0c;从而提高模型的性能并减少过拟合。本文将介绍如何使用 Python 的 Seaborn 和 Ma…

go 使用fyne实现桌面程序的计算器例子

使用Fyne工具包构建跨平台应用是非常简单的&#xff0c;在此之前我们需要做一些准备功能做&#xff0c;比如安装一些gcc基础图形依赖库&#xff0c;还有go语言本身的运行开发环境都是必要的。 在此之前我们希望你是go语言的已入门用户&#xff0c;掌握go的协程&#xff0c;管道…

Linux基础知识 - C(自学使用)

1.C语言基础知识 参考博客&#xff1a; https://blog.csdn.net/qq_45254369/article/details/126023482?ops_request_misc%257B%2522request%255Fid%2522%253A%252277629891-A0F3-4EFC-B1AC-410093596085%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%…

Xcode文件默认存储位置-使用c++file保存文件默认路径以及设置为路径为当前项目路径

Xcode文件默认存储位置-使用cfile保存文件默认路径以及设置为路径为当前项目路径 1.概述 使用Xcode工具开发时候&#xff0c;遇到C调用file创建文件后&#xff0c;在当前项目中找不到文件路径。这是由于xcode会将文件保存到默认设置的路径。下面是查看文件默认存储路径和修改…

DC-9靶场渗透

靶机&#xff1a;DC-9 DC: 9 ~ VulnHub 攻击机&#xff1a;kail linux 2024 1,将两台虚拟机网络连接都改为NAT模式&#xff0c;并查看DC-9的MAC地址 2&#xff0c;进行主机扫描&#xff0c;通过MAC地址发现靶机的IP地址 攻击机IP地址192.168.23.169&#xff0c;靶机IP地址192.1…

MySQL-存储过程/函数/触发器

文章目录 什么是存储过程存储过程的优缺点存储过程的基本使用存储过程的创建存储过程的调用存储过程的删除存储过程的查看delimiter命令 MySQL中的变量系统变量用户变量局部变量参数 if语句case语句while循环repeat循环loop循环游标cursor捕获异常并处理存储函数触发器触发器概…

16. 虚拟化

文章目录 第16章 虚拟化16.1 共享资源16.2 虚拟机16.3 虚拟机镜像16.4 容器16.5 容器和虚拟机16.6 容器的可移植性16.7 Pod&#xff08;容器组&#xff09;16.8 无服务器架构16.9 小结16.10 扩展阅读16.11 问题讨论 第16章 虚拟化 “虚拟”意味着永远不知道你的下一个字节从哪里…

SpringBoot poi-tl通过模板占位符生成word文件

简介&#xff1a; 开发中我们需要通过在word中使用占位符来动态渲染一些数据&#xff0c;本文讲解poi-tl实现动态生成word文档&#xff0c;包括表格循环&#xff0c;对象嵌套。 poi-tl官网文档 Poi-tl Documentation 1. word格式 这是我的test.word 这是导出后的out.docx文件 …

UE4 材质学习笔记12(水体反射和折射)

一.水体反射和折射 首先就是要断开所有连接到根节点的线&#xff0c;因为水有很多不同的节点成分&#xff0c;当所有其他节点都在用时 要分辨出其中一个是何效果是很难的。 虚幻有五种不同的方法可以创建反射&#xff0c;虚幻中的大多数场景使用多种这些方法 它们会同时运作。…

ctfshow-文件上传-151-161

CTFshow文件上传 PHP文件上传&#xff1a;1、代码思路 黑名单是设置不能通过的用户&#xff0c;黑名单以外的用户都能通过。 phtml、pht、php3、php4、php5后缀都会按做php文件执行&#xff0c;且不在黑名单内。 2、绕过 找漏网之鱼:cer、php3、php4、phtml等。 大小写绕…

反编译华为-研究功耗联网监控日志

摘要 待机功耗中联网目前已知的盲点&#xff1a;App自己都不知道的push类型的被动联网、app下载场景所需时长、组播联网、路由器打醒AP。 竞品 策略 华为 灭屏使用handler定时检测&#xff08;若灭屏30分钟内则周期1分钟&#xff0c;否则为2分钟&#xff09;&#xff0c;检…

第13次CCF CSP认证真题解

1、跳一跳 题目链接&#xff1a;https://sim.csp.thusaac.com/contest/13/problem/0 本题是小游戏“跳一跳”的模拟题&#xff0c;按照题意模拟即可。 100分代码&#xff1a; #include <iostream> using namespace std; int main(int argc, char *argv[]) {int n 30…

中国人寿财险青岛市分公司:科技保险,助力企业高质量发展

中国人寿财险青岛市分公司深知科技保险对于企业发展的重要性&#xff0c;积极将科技保险融入企业发展战略。公司利用科技手段&#xff0c;为企业提供风险评估、防灾减灾等增值服务&#xff0c;帮助企业降低经营风险。同时&#xff0c;公司还通过科技保险产品&#xff0c;为企业…