计算机网络 --- Socket 编程

news2024/9/23 16:45:41

序言

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


端口号

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);
 }

 // 获取 IP
 std::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 => 0x7F000001
 address.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.coloradmin.cn/o/2158003.html

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

相关文章

常⻅中间件漏洞(WebLogic)靶场

1.后台弱⼝令GetShell 启动环境 默认账号密码&#xff1a;weblogic/Oracle123 weblogic常⽤弱⼝令&#xff1a;https://cirt.net/passwords?criteriaweblogic 这⾥注意&#xff0c; 单个账号错误密码5次之后就会⾃动锁定。 172.16.1.51:7001/console/login/LoginForm.jsp …

视频剪辑软件排行榜前十名推荐!从入门到专业领域都有!

随着短视频的流行&#xff0c;视频剪辑已成为表达创意、分享故事、获取流量的重要工具。无论是专业视频制作人还是业余爱好者&#xff0c;选择一款合适的视频剪辑软件都至关重要。今天&#xff0c;我们就来盘点一下视频剪辑软件排行榜前十名&#xff0c;帮助你找到最适合自己的…

JavaScript是如何来的~~

文章目录 前言一、网络的诞生 ( The birth of the Web )二、Mosaic 浏览器三、Netscape 浏览器四、JavaScript的诞生 ~ 千呼万唤始出来总结 前言 例如&#xff1a;想要了解一门语言的发展历程&#xff0c;首先你得知道它是怎么来的&#xff0c;所以本文开篇介绍了网络的基本发…

【行为树】01-第一棵树

如何创建一个行为树 行为树,类似于状态机,无非是在正确的时间在正确的条件下调用回调的一种机制。 此外,我们将会交替使用__“callback"和"tick”__这两个词。 这些回调函数里发生的事情取决于你。 在这个教程系列中,大部分时间,Actions 只会在控制台上打印一…

中国土地利用覆盖和变化数据集(1980-2021)

该数据集通过融合森林资源清查数据和20种遥感土地利用产品&#xff0c;重建生成了1980-2015年中国森林覆盖数据集&#xff0c;空间分辨率为11公里。并且在此基础上进一步获得高精度森林覆被信息和土地利用覆盖数据集相融合&#xff0c;生成了中国1980-2021年土地利用覆盖和变化…

Minio上传url资源文件,文件内容不全的问题

遇到问题 使用minio-client时候上传文件为url链接时候&#xff0c;上传inputstream流出现了文件上传成功&#xff0c;但是文件内容缺失&#xff0c;无法正常打开&#xff01; 先看看基本的依赖和配置代码&#xff1a; pom.xml依赖 <!-- tika MIME检测机制 --><depen…

Android NestedScrollView+TabLayout+ViewPager+ 其它布局,ViewPager 不显示以及超出屏幕不显示问题

前言 此场景为 NestedScrollView 嵌套多个布局 &#xff0c;大致结构为 NestedScrollViewTabLayoutViewPagerfragment 其它View,如下图 &#xff0c; 一、ViewPager 设置高度才会显示内容问题 原因&#xff1a;NestedScrollView 计算高度先于 ViewPager 渲染前&#xff0c;所…

电商跨境电商商城系统/网上商城接口/电商数据接口详情

电商API接口背景&#xff1a;电商运营中&#xff0c;数据分析这项工作越来越重要&#xff0c;许多品牌方也越来越热衷去做电商数据分析。不过&#xff0c;全面的数据该如何获取呢&#xff0c;此时&#xff0c;电商数据接口的重要性便凸显出来了。 电商API数据接口主要有以下特…

go 读取excel

一、安装依赖 go get github.com/tealeg/xlsx二、main.go package mainimport "fmt" import "github.com/tealeg/xlsx"type Student struct {Name stringSex string }func (student Student) show() {fmt.Printf("Name:%s Sex:%s\r\n", stude…

OneNet平台替换腾讯云IOT平台(2024年6月)

文章目录 一、前言二、OneNet替换腾讯云IOT三、修改说明&#xff1a;STM32代码3.1 替换MQTT三元组&#xff08;常规操作&#xff09;3.2 修改上传的数据格式。 四、修改说明&#xff1a;服务器配置 一、前言 从2024年06月20日起&#xff0c; 新注册腾讯云物联网开发平台的用户…

浅谈人工智能技术,对社会经济变革的思考

原创 冰锋血骨 芯原创 2024年09月23日 15:44 北京 英国DeepMind公司研发的AlphaGo在2016年3月第一次战胜了围棋世界冠军韩国棋手李世石,人工智能(AI,Artificial Intelligence)第一次映入公众的视野。人工智能是什么?人工智能会想人一样思考吗?人工智能可以应用在哪些领域…

如何用AI论文生成工具撰写一篇高质量的成人教育毕业论文

撰写一篇高质量的成人教育毕业论文并不简单&#xff0c;它有一定的步骤和策略。锐智AI今天就总结了一些关键的步骤&#xff0c;希望对即将毕业的你顺利完成论文写作&#xff1a; 介绍之前简单说下锐智AI&#xff0c;它是一款集论文大纲生成、内容填充、文献引用、查重修改于一…

猎板PCB揭秘厚铜板技术的最新进展

在电子设备不断追求更高性能与更小尺寸的今天&#xff0c;PCB厚铜板技术因其卓越的导电性和散热性能&#xff0c;成为提升电子产品性能的关键材料。猎板PCB今天针对厚铜板的最新技术进展、应用领域及制造流程&#xff0c;为您提供全面的技术资料。 一、PCB厚铜板技术概览 PCB厚…

SpringCloud入门(六)Nacos注册中心(下)

一、Nacos环境隔离 Nacos提供了namespace来实现环境隔离功能。 nacos中可以有多个namespace。namespace下可以有group、service等。不同namespace之间相互隔离&#xff0c;例如不同namespace的服务互相不可见。 使用Nacos Namespace 环境隔离 步骤&#xff1a; 1.在Nacos控制…

007——递归(树的前置知识点)

目录 创建副本 递归 直接调用 间接调用 递归的具体流程又是什么样子的&#xff1f; 递归函数的组成&#xff1a; 递归可以用来解决什么问题&#xff1f; 例子1&#xff1a;求和问题 例子2&#xff1a;斐波那契数列 补充&#xff1a; 说到递归&#xff0c;我们可以简单…

Spring Boot实用小技巧5 - 第527篇

《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 《国内最全的Spring Boot系列之七》 SpringBoot …

10KV并网分布式光伏电力监控解决方案

一、分布式光伏并网要求 Q/GDW1480-2015 《分布式电源接入电网技术规定》&#xff1a;分布式电源并网电压等级可根据各并网点装机容量进行初步选择&#xff0c;推荐如下&#xff1a; 8kW 及以下可接入220V&#xff1b; 8kW~400kW可接入380V&#xff1b; 400kW~6MW可接入10k…

MySQL篇(锁机制 基本介绍、全局锁\表级锁\行锁、悲观锁\乐观锁)

目录 讲解一&#xff1a;基本介绍 一、简介 二、MySQL中的锁 1. 锁粒度分类&#xff08;三类&#xff09; 讲解二&#xff1a;全局锁\表级锁\行锁 一、全局锁 1. 简介 2. 不加全局锁的问题 3. 加全局锁的好处 4. 操作 加全局锁 数据备份 释放锁 5. 特点 二、表级…

计算机毕业设计 基于Flask+Vue的博客系统 Python毕业设计 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

稀土抗菌剂马桶盖,让家庭清洁更上一层楼

稀土元素&#xff0c;作为现代工业中的“维生素”&#xff0c;在抗菌领域的表现尤为突出。稀土抗菌剂是一种通过阻碍细菌代谢活动&#xff0c;抑制细菌繁殖的高效抗菌材料。与普通抗菌技术相比&#xff0c;稀土抗菌剂具有更强的持久性和安全性&#xff0c;能够长期有效地抑制细…