网络基础(2)

news2025/8/6 10:10:46

目录

    • 1. 端口号
    • 2. 套接字socket
    • 3. 网络通信
      • 3.1 sockaddr与sockaddr_in
      • 3.2 接口
        • 服务端
        • 3.2.1 创建套接字,打开网络文件
        • 3.2.2 给该服务器绑定端口和ip(特殊处理)
        • 3.2.3 初始化相关服务器
        • 3.2.4 提供服务
        • 客户端
        • 3.2.5 绑定
        • 3.2.6 使用服务
    • 4. makefile实现:
    • 5. 整体代码

1. 端口号

IP能够唯一地标识互联网中的一台主机;而端口号能够唯一地标识一台机器上的唯一一个进程。

那么这两个因素加起来,就构成了能够标识互联网上的唯一一个进程。这就是端口号与IP的联系。

2. 套接字socket

整个网络看作是一个大的OS,所有网络上网行为,都是在这里面实现进程间通信的。

IP地址+port端口号 = socket

端口号之于进程PID,就相当于身份证号之于学生学号。

  • 为什么有了PID,在网络里还需要端口号来进行标识?

这就好比学校不用身份证来对学生信息进行管理、录入。因为学校内部会有更合适的管理系统,采用自己一套的管理方式能够使管理更加得心应手,也不惧怕外部环境的改变,实现与外部解耦。PID和端口号也是这个原理。

3. 网络通信

3.1 sockaddr与sockaddr_in

要实现网络通信,首先是要找到目标主机,其次是找到该主机上的目标进程。

而进程具有独立性,要实现网络通信需要让两个进程看到同一份资源,这份资源就是网络。

应用层的下一层就是传输层,传输层有TCP/UDP协议。

  • 统一接口

对于将要介绍的sock接口,要先说明一个概念,就是sockarr结构:由于各种网络协议的实现方式不同,导致接口需要对应设计,所以干脆就设计出来了一个通用的接口,该参数统一为sockaddr。

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及UNIX DomainSocket。 然而, 各种网络协议的地址格式并不相同。

目前我们采用的是struct sockaddr,这是一个通用接口,也即是说,可以兼容后两个接口类型的参数。

通用接口与其他两个不同协议的接口:

在这里插入图片描述

虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in; 这个结构里主要有三部分信息: 地址类型, 端口号, IP地址。

sockaddr与sockaddr_in其实都是结构体类型:

  1. sockaddr

它把目标地址和端口信息实现在一起:

struct sockaddr{
	sa_family_t sin_family;//16位地址类型家族
	char sa_data[14];
	//14位长度地址数据,包含套接字中的目标地址和端口信息
}
  1. sockaddr_in

sockaddr_in的头文件包含在:

#include <netinet/in.h>
#include <arpa/inet.h>

该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下

typedef uint16_t in_port_t;//十六位数字

struct sockaddr_in{
	sa_family_t   sin_family;//16位地址类型家族
	in_port_t sin_port;//16位端口号
	struct in_addr    sin_addr;//32位ip地址
	unsigned char sin_zero;//未使用
}

in_addr其实就是32位的IPv4地址:

typedef uint32_t in_addr_t;

struct in_addr{
	In_addr_t  s_addr;    //32位IPv4地址
};
  1. sockaddr_un
struct sockaddr_un{
	//16位地址类型
	//108字节路径名
}

IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。

socket API可以都用struct sockaddr * 类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。

3.2 接口

服务端

3.2.1 创建套接字,打开网络文件

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
  1. 参数:

domain就是用来区分传入的是哪个协议种类。

type代表协议特性,是套接字类型。比如流式套结、原始套结等。

proyocol在tcp中只需要全部设置为0。

  1. 返回值:创建成功后,新的套接字会返回一个文件描述符。
#include <iostream>
#include <cerrno>
int main()
{
    int sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock < 0){
        std::cout<<"socket create error!"<<errno<<std::endl;
        return 1;
    }
    std::cout<<"sock:"<<sock<<std::endl;
    return 0;
}

运行结果:

在这里插入图片描述

3.2.2 给该服务器绑定端口和ip(特殊处理)

作为一个服务器,对应的服务器地址(IP+port)是必须要被客户(人、软件、浏览器)所知晓的,并且不能轻易地被改变。

  • 首先要绑定端口号
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int sockfd, const struct sockaddr *addr,
socklen_t address_len);
  1. 参数

sockfd代表创建好的套接字的文件描述符;addr代表需要用户指定服务器的相关socket信息,address_len代表传入的结构体大小。

  1. 返回值

绑定成功返回0,失败返回-1,也可以是错误码。

3.2.3 初始化相关服务器

这里的参数addr是相关服务器的信息,所以要bind,就要先初始化好addr的信息:

首先定义一个sockaddr_in类型的结构体,因为我们平时传的addr其实是sockaddr_in类型的,只需要在绑定的时候做一下强转即可。

那么接下来就是将它里面的对应字段全部初始化:

const uint16_t port = 8080;
struct sockaddr_in local;
local.sin_family = AF_INET; //IPV4
local.sin_port = htons(port); //该端口号是主机序列,要改成网络序列
local.sin_addr.s_addr = INADDR_ANY;

其中port是主机序列,需要转为网络序列,使用的是htons函数:

h是host代表主机,n代表network网络:

在这里插入图片描述

剩下最后一个字段初始化为INADDR_ANY的意思是:由该服务器发送的,到任意主机(IP)上的数据都能被你的网络进程接收。

如果不这样初始化,而是绑定了确定的IP,例如:

//inet_addr函数将地址从数点法转化为整数IP
//网络需要的是整数表示IP而不是数点法表示的地址
local.sin_addr.s_addr = inet_addr("xx.xxx.xx.xxx");

这样的话,只有发送到该IP的数据才能被你进程接收,所以一般不这么写。

并且云服务器不直接绑定公网IP。

然后就可以进行绑定:

if(bind(sock,(struct sockaddr*)&lockal,sizeof(local)) < 0){
    std::cout << "bind error : " << errno << std::endl;
    return 2;
}

3.2.4 提供服务

绑定完成以后,等待他人给自己的服务器发送信息,是接收的过程。

  • recvfrom

udp的数据读取不是用文件的接口,而是有专门的接口recvfrom:

在这里插入图片描述

参数:分别是自身套接字fd、读完的数据放到buf、读len大小、读的方式flag(默认0),剩下最后两个代表的是和你的服务器通信的客户端的信息,是一个输出型参数,作用是如需要返回数据给它,就可以拿到其地址返回。

  • sendto

给对端发信息,用的是sendto

其他参数都一样,最后两个参数代表的是要给谁发:

在这里插入图片描述

由此可以提供服务:

// 3. 提供服务
#define NUM 1024
char buffer[NUM];
bool quit = false;
while(!quit){
    struct sockaddr_in send;
    socklen_t len = sizeof(send);
    //开始读
    recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&send,&len);
    std::cout << "client" << buffer << std::endl;
    //收到以后回复hello
    std::string echo = "hello";
    sendto(sock,echo.c_str(),echo.size(),0,(struct sockaddr*)&send,len);
    }

客户端

3.2.5 绑定

客户端不需要显示绑定。

对于客户端来说,也必须要有套接字,但是它不需要显示绑定。因为一旦绑定了,它就要和某一个端口关联了,该端口不一定会存在,或者被占用,那么该客户端将无法使用。

对于服务端来说,端口必须要明确,并且不能变更;而对于客户端,只要有端口就行,因为是客户端访问他人。所以客户端一般都是由OS在发送数据的时候自动绑定,采用的是随机端口。

// 1. 创建
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0){
    std::cout<<"socket create error!"<<errno<<std::endl;
    return 1;
}
std::cout<<"sock:"<<sockfd<<std::endl;

3.2.6 使用服务

实现基本和服务端一样,但是由于自定义遵守./udp_client server_ip server_port的格式,所以要用到命令行参数来规范行为,不规范的话会打印使用手册。

void Usage(std::string proc)
{
    std::cout << "Usage: \n\t"<< proc << " server_ip server_port" << std::endl;
}
int main(int argc,char* argv[])
{
    if(argc != 3){
        Usage(argv[0]);
        return 0;
    }
    // 1. 创建
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd < 0){
        std::cout<<"socket create error!"<<errno<<std::endl;
        return 1;
    }
    std::cout<<"client:sock:"<<sockfd<<std::endl;
    //客户端也必须要有套接字
    //不需要显示bind
    // 2. 使用服务
    // ./udp_client server_ip server_port的格式
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr(argv[1]);
    while(1){
        // 1. 数据来源 
        std::string s;
        std::cout<<"输入# ";
        std::cin>>s;
        // 2. 发给谁
        sendto(sockfd,s.c_str(),s.size(),0,(struct sockaddr*)&server,sizeof(server));
        //此处tmp就是一个”占位符“
        struct sockaddr_in tmp;
        socklen_t len = sizeof(tmp);
        char buffer[1024];
        recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&tmp, &len);
    std::cout << "server echo# " << buffer << std::endl;
    }
    return 0; 
}

由于云服务器需要手动在后台开通窗口才可通信,所以IP使用127.0.0.1来测试

下列运行结果:

客户端:
在这里插入图片描述

服务端:

在这里插入图片描述

4. makefile实现:

.PHONY:all
all:udp_server udp_client

udp_server:udp_server.cc
	g++ -o $@ $^ -std=c++11

udp_client:udp_client.cc
	g++ -o $@ $^ -std=c++11

.PHONY:clean
clean:
	rm -f udp_server udp_client

5. 整体代码

服务端:

#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <cerrno>

const uint16_t port = 8080;
int main()
{
    // 1. 创建套接字,打开网络文件
    int sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock < 0){
        std::cout<<"socket create error!"<<errno<<std::endl;
        return 1;
    }
    std::cout<<"server:sock:"<<sock<<std::endl;
    
    // 2. 给该服务器绑定端口和ip
    struct sockaddr_in local;
    local.sin_family = AF_INET; //IPV4
    local.sin_port = htons(port); //该端口号是主机序列,要改成网络序列
    local.sin_addr.s_addr = INADDR_ANY;

    if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0){
        std::cout << "bind error : " << errno << std::endl;
        return 2;
    }
    // 3. 提供服务
    #define NUM 1024
    char buffer[NUM];
    bool quit = false;
    while(!quit){
        struct sockaddr_in send;
        socklen_t len = sizeof(send);
        recvfrom(sock,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&send,&len);
        std::cout << "client say# " << buffer << std::endl;
        //收到以后回复hello
        std::string echo = "hello";
        sendto(sock,echo.c_str(),echo.size(),0,(struct sockaddr*)&send,len);
    }
    return 0;
}

客户端:

#include <iostream>
#include <string>
#include <cerrno>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//正确用法
// ./udp_client server_ip server_port的格式
void Usage(std::string proc)
{
    std::cout << "Usage: \n\t"<< proc << " server_ip server_port" << std::endl;
}
int main(int argc,char* argv[])
{
    if(argc != 3){
        Usage(argv[0]);
        return 0;
    }
    // 1. 创建
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd < 0){
        std::cout<<"socket create error!"<<errno<<std::endl;
        return 1;
    }
    std::cout<<"client:sock:"<<sockfd<<std::endl;
    //客户端也必须要有套接字
    //不需要显示bind
    // 2. 使用服务
    // ./udp_client server_ip server_port的格式
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    server.sin_addr.s_addr = inet_addr(argv[1]);
    while(1){
        // 1. 数据来源 
        std::string s;
        std::cout<<"输入# ";
        std::cin>>s;
        // 2. 发给谁
        sendto(sockfd,s.c_str(),s.size(),0,(struct sockaddr*)&server,sizeof(server));
        //此处tmp就是一个”占位符“
        struct sockaddr_in tmp;
        socklen_t len = sizeof(tmp);
        char buffer[1024];
        recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&tmp, &len);
    std::cout << "server echo# " << buffer << std::endl;
    }
    return 0; 
}

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

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

相关文章

关于 JavaScript 中的 Promises

在 JavaScript 中&#xff0c;Promise 是一个对象&#xff0c;它表示一个可能还不可用&#xff0c;但会在未来解决的值。Promises 用于处理异步操作&#xff0c;例如发出网络请求或访问数据库&#xff0c;其中结果不是立即可用的。如果你准备好了&#xff0c;我想开始我们的冒险…

基于SpringBoot+SpringCloud+Vue前后端分离项目实战 --开篇

本文目录前言做项目的三大好处强强联手(天狗组合)专栏作者简介专栏的优势后端规划1. SpringBoot 和 SpringCloud 的选择2. Mybatis 和 MybatisPlus 和 JPA 的选择3. MySQL 和 Mongodb 的选择4. Redis 和 RocketMQ5. 后端规划小总结后端大纲提前掌握的知识点一期SpringBoot二期S…

EventLoop机制

JavaScript 是单线程的语言 JavaScript 是一门单线程执行的编程语言。也就是说&#xff0c;同一时间只能做一件事情。 单线程执行任务队列的问题&#xff1a; 如果前一个任务非常耗时&#xff0c;则后续的任务就不得不一直等待&#xff0c;从而导致程序假死的问题。 同步任…

机器学习和深度学习的综述

机器学习和深度学习的综述 1.1.1人工智能、机器学习、深度学习的关系 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;是最宽泛的概念&#xff0c;是研发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。由于这个定义…

代码随想录算法训练营第二十一天打卡 | 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

打卡第21天&#xff0c;继续二叉树&#xff0c;前几天终于补完了&#xff0c;感觉难度上来了。 今日任务 530.二叉搜索树的最小绝对差501.二叉搜索树中的众数 二叉树的最近公共祖先 530.二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不…

计算机网络——路由、交换、子网篇

子网掩码 解释1 作者&#xff1a;纪言 链接&#xff1a;https://www.zhihu.com/question/56895036/answer/447915532 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 仅限ip4首先子网掩码这名字取得很好&#xff0c…

Java创建线程的方式只有一种:Thread+Runnable

Java创建线程的方式其实只有一种&#x1f468;‍&#x1f393;一、继承Thread&#x1f468;‍&#x1f393;二、实现Runnable接口&#x1f468;‍&#x1f393;三、实现Callable接口&#x1f468;‍&#x1f393;四、通过线程池创建&#x1f468;‍&#x1f393;五、总结一般我…

SOLID 原则要这么理解!

什么是 SOLID 原则 SOLID 原则其实是用来指导软件设计的&#xff0c;它一共分为五条设计原则&#xff0c;分别是&#xff1a; 单一职责原则&#xff08;SRP&#xff09;开闭原则&#xff08;OCP&#xff09;里氏替换原则&#xff08;LSP&#xff09;接口隔离原则&#xff08;…

Matter 系列 #9|乐鑫 Matter 预配置服务加速设备生产

乐鑫 Matter 系列文章 #9 目录 Matter 预配置服务 1. 设备认证 (Device Attestation) 2. 独特性 (Uniqueness) 3. 安全性 (Security) 联系我们​​​​​​​ 如今&#xff0c;物联网行业蓬勃发展&#xff0c;大量市场参与者正在积极地构建 Matter 智能设备。 乐鑫一直致…

盲盒开发:多元化设计、数字化销售、可持续发展

随着互联网的发展&#xff0c;盲盒成为了一种新型文化现象和玩具消费方式。它不仅满足了人们的消费需求&#xff0c;也引发了人们对玩具和收藏品的热爱和追求。目前&#xff0c;盲盒市场已经逐渐发展成为一个庞大的产业&#xff0c;其中海外盲盒市场更是异彩纷呈&#xff0c;备…

Git安装初始化及Gitee上传提交步骤

一、git安装git下载https://git-scm.com/download/win直接点击下一步“next”安装即可在桌面右键&#xff0c;选择 Git Bash Here 在 Git Bash Here 终端里输入 git --version 查看 git 版本&#xff0c;如图所示&#xff0c;说明 Git 安装成功。在终端设置用户签名邮箱git con…

windows7安装sql server 2000安装步骤 及安装过程中遇到的问题和解决方式

提示&#xff1a;文章写完后windows7安装sql server 2000安装步骤 及安装过程中遇到的问题和解决方式&#xff0c; 文章目录一、ms sql server 2000是什么&#xff1f;版本简介&#xff1a;**特点&#xff1a;****优点&#xff1a;**二、步骤1.下载安装包及Sq4补丁包2.安装 ms …

淘宝 APP 网络架构演进与弱网破障实践

作者&#xff1a;沈良炜 阿里大淘宝终端体验平台团队 面对移动互联网络下复杂多变的网络环境&#xff0c;如何提供更稳定可靠的请求性能&#xff0c;保障用户的加载浏览体验&#xff1f;本文将为大家分享淘宝APP统一网络库的演进。 一、引言 自2013年ALLIN无线到今天&#xff0…

ESP32设备驱动-土壤湿度传感器驱动

土壤湿度传感器驱动 1、土壤湿度传感器介绍 土壤湿度传感器由两个探头组成,用于测量水的体积含量。 两个探头让电流通过土壤,然后得到电阻值来测量水分值。 当有更多的水时,土壤会传导更多的电,这意味着电阻会更小。 因此,水分含量会更高。 干燥的土壤导电性差,所以当…

第八章.贝叶斯分析—贝叶斯定理朴素贝叶斯

第八章.贝叶斯分析 8.1 贝叶斯定理&朴素贝叶斯 贝叶斯主要应用与新闻分类&#xff0c;文本分类&#xff0c;评论分析。 1.数理统计学处理的信息 1).贝叶斯方法 关于统计推断的系统理论和方法&#xff0c;称为贝叶斯方法。 2).经典统计学 总体信息&#xff1a;当前总体样…

【2223sW2】LOG2

写在前面 好好学习&#xff0c;走出宿舍&#xff0c;走向毕设&#xff01; 一些心路历程记录&#xff0c;很少有代码出现 因为鬼知道哪条代码到时候变成毕设的一部分了咧&#xff0c;还是不要给自己的查重挖坑罢了 23.3.2 检验FFT 早上师兄帮忙看了一眼我画的丑图&#xff…

什么是模块,Python模块化编程(入门必读)

Python 提供了强大的模块支持&#xff0c;主要体现在&#xff0c;不仅 Python 标准库中包含了大量的模块&#xff08;称为标准模块&#xff09;&#xff0c;还有大量的第三方模块&#xff0c;开发者自己也可以开发自定义模块。通过这些强大的模块可以极大地提高开发者的开发效率…

深入理解Mysql索引底层数据结构与算法

索引是帮助MySQL高效获取数据的排好序的数据结构 深入理解Mysql索引底层数据结构与算法1.常见的数据结构讲解1.1 二叉树1.1.1 二叉树的定义1.1.2 二叉树示例1.1.3 Mysql为什么不使用二叉树进行数据存储1.2 红黑树1.2.1 红黑树的定义1.2.2 红黑树示例1.2.3 Mysql 为什么不适用红…

k8s学习之路 | k8s 工作负载 ReplicaSet

文章目录1. ReplicaSet 基础概念1.1 RS 是什么&#xff1f;1.2 RS 工作原理1.3 什么时候使用 RS1.4 RS 示例1.5 非模板 Pod 的获得1.6 编写 RS1.7 使用 RS1.8 RS 替代方案2. ReplicaSet 与 ReplicationController2.1 关于 RS、RC2.2 两者的选择器区别2.3 总结1. ReplicaSet 基础…

【三维几何学习】使用VTK对网格输入特征进行可视化

使用VTK对网格输入特征进行可视化引言一、全部代码二、可视化引言 使用python调用VTK库对网格的输入特征进行可视化&#xff0c;方便后续实验与分析 上图可视化的输入特征是热核特征HKS的第一个通道&#xff0c;也可对其他输入进行可视化数据集可参考1:三角网格(Triangular Me…