计算机网络 --- Socket 编程

news/2024/9/20 13:59:24/

序言

 在上一篇文章中,我们介绍了 协议,协议就是一种约定,规范了双方通信需要遵循的规则、格式和流程,以确保信息能够被准确地传递、接收和理解。
 在这篇文章中我们将介绍怎么进行跨网络数据传输,在这一过程中相信大家肯定可以加深对协议的理解。


端口号

1. 端口号的作用

 我们已经理解了什么是 IPIP 用于标识互联网上的每个设备。我们可以通过他,将数据包能够从一个设备跨网络传输到另一个设备。但是数据发送到设备上,还需要正确地被处理这才是目的吧!
 举个栗子,大家平时也刷抖音吧!我们所看到的视频就是抖音平台所跨网络传输给我们的数据,但是有没有可能我们手机在刷抖音的同时还有其他程序也正在运行。那么数据是怎么正确地被抖音所接受的而不是其他程序。
 每一个运行的程序以进程的方式存在于内存中,所以抖音肯定也是。所以我们使用唯一的端口来标识内存中需要进行网络传输的进程,当数据到达设备时就会根据端口号选择进程

2. 再识端口号

 端口号存在于传输层协议层:

  • 端口号是一个 2 字节 16 位的整数
  • 端口号用来 标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理
  • IP 地址 + 端口号 能够标识网络上的某一台主机的某一个进程
  • 一个端口号只能被一个进程占用

第三点大家如何理解呢?一个 IP地址 标识的是网络是唯一的设备,而 端口号 标识的是设备中唯一的一个进程,两者一起就是标识 网络上的某一台主机的某一个进程
 所以实际上的网络传输,不就是跨设备跨网络的进程间通信吗?

3. 端口号的需求

 服务端在运行时需要指定一个固定的端口号,这样客户端才能根据你的 IP地址,端口号 来找到需要进行通信的进程。但是端口号也不是随便取的,是有要求的:

  • 0 - 1023: 知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议, 他们的
    端口号都是固定的.
  • 1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作
    系统从这个范围分配的.

所以说我们只能在 1024 - 65535 进行选择。

 客户端就稍显不同了,客户端就不需要指定一个固定的端口号,这又是为什么呢?对于一个服务器来说,他的设备上只是运行了他的业务程序,而对于我们客户端来说,我们的设备上可能同一时间运行着很多程序。如果每一个客户端都固定一个端口的话,很 可能不同的客户端之间就会造成冲突!所以为了避免这种情况,每次运行时操作系统都会为客户端需要跨网络通信的程序自动分配一个端口


简单认识传输层协议

在传输层有很多协议(不同的传输方式),我们主要简单介绍两种 UDP, TCP ,在这里只是简单介绍,在后面会详细原理。

1. UDP 协议

UDP协议 适用于 实时性要求较高、对数据可靠性 要求较低的应用场景,如音频、视频传输(流媒体)、DNS解析、广播和多播等:

  • UDP 是一种 无连接 的传输层协议,提供面向事务的简单不可靠信息传送服务。
  • 数据以数据报的形式独立发送,不保证数据的可靠性、顺序性和完整性。
  • UDP 协议开销小,传输速度快,适用于对实时性要求较高、对数据可靠性要求较低的应用场景。

2. TCP 协议

TCP协议 适用于对数据完整性、顺序性要求较高的应用场景,如网页浏览(HTTP)、文件传输(FTP)、邮件传输(SMTP)等:

  • TCP 是一种 面向连接的、可靠的、基于字节流 的传输层协议。
  • 在通信双方之间建立一个虚拟的连接,然后在这个连接上进行数据的传输和控制。连接的建立和释放需要经过三次握手和四次挥手的过程。
  • 通过 序号、确认号、重传机制、校验 和等手段,保证数据在传输过程中不会出现丢失、重复、乱序或错误的情况。

3. 总结

 现在大家就需要简单理解为 UDP 是不可靠的,数据传输可能会丢失部分信息,而 TCP 是可靠的,数据传输的完整性高。大家就会觉得,那我以后肯定选后后面的呀,因为他 可靠 嘛!不是这样的,可靠也是需要代价的,需要你有稳定且高速的网络服务!
 协议的选择要看具体的场景!就比如视频的传输就最好选在前者, 所以你看视频的时候偶尔会卡一下,但无关大雅!传文件就选后者,因为你需要你的文件是完好的,文件如果传过来丢失一部分数据那还怎么看!


网络字节序

1. 什么是网络字节序

 大家知道一个概念叫做 大小端 吗?大端机是指将数据的高位存储到内存的低地址,而小端机是指将数据的低位存储到地址的低地址:
在这里插入图片描述

所以很可能你的设备是大端机,而需要接受数据的设备是小端机,为了解决这个问题提出了 网络字节序

  • TCP/IP协议 规定,网络数据流应采用 大端字节序,即低地址高字节.
  • 不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送 / 接收数据
  • 如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可

2. 相关接口

 当然这个过程不需要我们自主实现,已经存在相应的接口了:
在这里插入图片描述
我们怎么来方便的记忆呢?h 代表 host(主机)n 代表 network(网络)l 代表 longs 代表 short。我们现在随便选一个来解释,就第三个吧:代表 网络字节序转主机字节序,32位


sockaddr 结构

 该结构体用于定义和存储 IPv4地址 以及相关的端口信息:
在这里插入图片描述

我们主要使用第二个结构体,第三个是用于一个主机上的进程间通信,那第一个是干嘛的呢?这个结构体本身只提供了一个非常基础的框架,不能进行跨网络通信或者是进程间通信,但他在底层 提供一个统一的接口,根据你第一个参数判断你的通信类型。这不就是 C语言 的多态吗?


UDP 网络编程

 在这个版本我们将使用 UDP协议 来进行网络编程,我们将实现一个客户端用于发送信息,服务端用于接收消息:

1. Server 服务端

 首先我们需要指定,启动程序时需要指定 IP 端口 :

int main(int argc, char* argvs[])
{if(argc != 3){std::cout << "Usage: ./server ip port" << std::endl;exit(1);}
}

我们根据相应的内容来初始化 struct sockaddr_in 结构体的相关内容:

// 初始化结构体字段
struct sockaddr_in address;
address.sin_family = AF_INET; // 网络通信
address.sin_addr.s_addr = inet_addr(IP.c_str()); // 将点分十进制的字符串改为长整型 127.0.0.1 => 0x7F000001
address.sin_port = htons(PORT); // 将端口号转化位网络字节序

之后我们创建套接字文件,我们的读取和发送数据都需要经过该文件:

// AF_INET 代表网络通信
// SOCK_DGRAM 代表 UDP 协议
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{perror("socket:");
}

现在我们将套接字结构体和文件绑定,代表该文件服务于指定端口:

// 绑定socket到端口 
int n = bind(sockfd, (struct sockaddr*)(&address), sizeof(address));
if(n < 0)
{perror("bind:");exit(0);
}

接收消息处理函数稍微有点长,但是思路是很简单的:

// 不断地接受客户端的信息
char msg[1024];
struct sockaddr_in clientAddr;
socklen_t len = sizeof(clientAddr);
while(true)
{int n = recvfrom(sockfd, msg, sizeof(msg), 0, (struct sockaddr*)(&clientAddr), &len); // 接收消息if(n < 0){perror("recvfrom:");continue;}else if(n == 0){std::cout << "Client Quit..." << std::endl;exit(0);}msg[n] = '\0';printf("[%s:%s]# %s\n", inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port),msg);
}

首先我们定义一个缓冲区 msg 接收返回值,之后通过 recvfrom 接受信息,之所以还需要传入一个 clientAddr 的原因是因为,这是发送方的信息,你总得知道谁发给你打吧?之后我们有三种情况:

  • 接受失败,返回 -1 。我们等待一下发送
  • 客户端退出,返回 0 。我们也退出
  • 成功接收,返回发送的字符数

inet_ntoa 这个该函数是将 IP地址 转化为我们熟悉的字符串形式,ntohs 将网络字节序的端口号转化为主机序列。

2. Client 客户端

 客户端有很多步骤是和服务端类似的,但是整体少简单因为:

  • client 端口不需要用户指定,OS 自动分配
  • client 不需要显示的绑定自己的端口和 IP
  • 在首次向服务器发送信息时,会自动绑定

所以我们直接先通过参数获取服务端的信息,并初始化对应结构体:

 if(argc != 3){std::cout << "Usage: ./server ip port" << std::endl;exit(1);}// 获取 IPstd::string IP = argv[1];// 获取端口号uint16_t PORT = std::stoi(argv[2]);// 初始化结构体字段struct sockaddr_in address;address.sin_family = AF_INET; // 网络通信address.sin_addr.s_addr = inet_addr(IP.c_str()); // 将点分十进制的字符串改为长整型 127.0.0.1 => 0x7F000001address.sin_port = htons(PORT); // 将端口号转化位网络字节序

现在我们还需要创建套接字文件:

 // AF_INET 代表网络通信// SOCK_DGRAM 代表 UDP 协议int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){perror("socket:");}

我们客户端不需要显示绑定端口和 IP,现在可以直接构造发送消息的逻辑:

// 不断地向服务端发送信息
std::string msg;
while(true)
{std::cout << "Please Enter# ";std::cin >> msg;ssize_t n = sendto(sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr*)(&address), sizeof(address));if(n < 0){perror("sendto:");continue;}
}

3. 总结

 其实很多时候服务端不需要指定 IP地址,因为一个设备可能有很多 IP地址,为了接受来自所有不同地址的请求,我们会设置:
address.sin_addr.s_addr = INADDR_ANY; 这代表接受所有 本设备 IP地址 的请求,处理数据。
 我们在之前提到过, UDP 是一种 无连接 的传输层协议。怎么体现呢?在这里我只是启动客户端程序,不启动服务端,然后发送消息:
在这里插入图片描述
可以看到即使是服务器不在线,我们依然能够发送消息!


TCP 网络编程

 我们将使用 TCP协议 来实现相同的功能:

1. Server 服务端

 前面的步骤都是一样的,接受端口号,初始化结构体字段,但是在创建套接字文件时,就需要更改一下选项了:

// SOCK_STREAM 代表 TCP 协议
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{perror("socket:");
}
std::cout << "Successful create sockfd..." << std::endl;

之后绑定也是一样的,但是我们不能直接使用该套接字进行收发消息!该文件用于监听,查看是否有客户端的请求:

// 监听,第二个参数大家先不管,后面理论会介绍
n = listen(sockfd, 3);
if(n < 0)
{perror("listen:");exit(1);
}
std::cout << "Successful listening..." << std::endl;

监听之后,当有链接请求发送时,我们需要接受该请求,系统会返回一个进行数据读写的文件描述符:

// 连接
struct sockaddr_in ClientAddress; // 用于接收客户端的信息
int newsockfd = accept(sockfd, (struct sockaddr*)(&ClientAddress), sizeof(ClientAddress));
if(newsockfd < 0)
{perror("connect:");exit(1);
}
std::cout << "Successful accept..." << std::endl;

之后便是接受信息哪些步骤:

// 不断地接受客户端的信息
char msg[1024];
while(true)
{int n = read(newsockfd, msg, sizeof(msg));if(n < 0){perror("read");continue;}else if(n == 0){std::cout << "Client Quit..." << std::endl;exit(0);}msg[n] = '\0';printf("[%s:%d]# %s\n", inet_ntoa(ClientAddress.sin_addr),ntohs(ClientAddress.sin_port),msg);
}

2. Client 客户端

 客户端的流程前面都一样,获取 IP地址,端口号,以及初始化结构体字段,创建套接字文件,但是他需要一次连接请求:

// 连接请求
int n = connect(sockfd, (struct sockaddr*)(&address), sizeof(address));
if(n < 0)
{perror("connect:");exit(1);
}
std::cout << "Successful connect..." << std::endl;

当连接成功时,就可以正常的通信了:

// 不断地向服务端发送信息
std::string msg;
while(true)
{std::cout << "Please Enter# ";std::cin >> msg;ssize_t n = send(sockfd, msg.c_str(), msg.size(), 0);if(n < 0){perror("send:");continue;}
}

3. 总结

TCP 是一种 面向连接的、可靠的、基于字节流 的传输层协议。现在我们不启动服务端,直接启动客户端发送消息:
在这里插入图片描述
可以看到直接拒绝我们的连接,和 UDP 截然不同。


总结

 在这篇文章中我们介绍了在实践上如何进行套接字编程,但是我们并没有深入的理解理论的知识,希望大家有所收获!


http://www.ppmy.cn/news/1528011.html

相关文章

AI对汽车行业的冲击和比亚迪新能源汽车市场占比

人工智能&#xff08;AI&#xff09;对汽车行业的冲击正在迅速改变该行业的面貌&#xff0c;从智能驾驶到生产自动化&#xff0c;再到个性化的消费者体验&#xff0c;AI带来的技术革新在各个层面影响着汽车产业。与此同时&#xff0c;新能源汽车市场&#xff0c;特别是以比亚迪…

Apollo自动驾驶项目(二:cyber框架分析)

Apollo 的 Cyber 框架 是一个基于消息传递的中间件&#xff0c;用于处理模块之间的通信和数据共享&#xff0c;类似于 ROS&#xff08;Robot Operating System&#xff09;。它是 Apollo 系统的核心框架之一&#xff0c;负责协调自动驾驶软件中不同模块的协同工作。Cyber 框架为…

【方案】智慧园区管理平台建设方案(Word原件)

1. 项目概述 2.项目建设的必要性 3.需求分析 4.建设方案 5.系统实施与运行维护方案 6.投资预算 7.效益分析 &#xff08;智慧园区建设方案PPT功能建设文档Word具体实现源码&#xff09; 软件资料清单列表部分文档清单&#xff1a;工作安排任务书&#xff0c;可行性分析报告&…

OpenGL(四) 纹理贴图

几何模型&材质&纹理 渲染一个物体需要&#xff1a; 几何模型&#xff1a;决定了物体的形状材质&#xff1a;绝对了当灯光照到上面时的作用效果纹理&#xff1a;决定了物体的外观 纹理对象 纹理有2D的&#xff0c;有3D的。2D图像就是一张图片&#xff0c;3D图像是在…

MPN – 制造商物料编号(延伸)

SAP从放弃到入门系列之-制造商零件编号-MPN 物料_sap mpn-CSDN博客 MPN – 制造零件号_hers物料类型-CSDN博客 启用制造商物料编号(MPN) – 枫竹丹青SAP学习与分享 (fenginfo.com) MPN在QM中的使用_sap q-info-CSDN博客 关于制造商物料&#xff0c;每篇文章侧重点不同&…

【Qt笔记】QTabWidget控件详解

目录 引言 一、基本功能 二、核心属性 2.1 标签页管理 2.2 标签位置 2.3 标签形状 2.4 标签可关闭性 2.5 标签可移动性 三、信号与槽 四、高级功能 4.1 动态添加和删除标签页 4.2 自定义标签页的关闭按钮行为 4.3 标签页的上下文菜单 五、样式设置 六、应用示例…

Pandas中的聚合函数,及空值计算(与numpy adarray对比)

目录 常用聚合函数 pandas中聚合函数的处理方式 Series对象使用聚合函数 DataFrame对象使用聚合函数 Pandas中聚合函数对空值的处理,及与numpy ndarray进行对比 常用聚合函数 FunctionDescription备注countNumber of non-NA observations个数sumSum of values求和meanMea…

【学术会议征稿】2024年先进控制系统与自动化技术国际学术会议(ACSAT 2024)

2024年先进控制系统与自动化技术国际学术会议&#xff08;ACSAT 2024&#xff09; 2024 International Conference on Advanced Control Systems and Automation Technologies 2024年先进控制系统与自动化技术国际学术会议&#xff08;ACSAT 2024&#xff09;将于2024年11月15…