socket地址理解

socket介绍

套接字的基本概念

1. 套接字的定义:

  • 套接字(socket)是计算机网络中用于通信的端点,它抽象了不同主机上应用进程之间双向通信的机制。

2. 套接字的作用:

  • 套接字连接应用进程与网络协议栈,使得应用程序能够利用网络协议进行通信。
  • 它作为应用程序与网络协议栈的接口,通过操作系统提供的 API 进行数据交换。
套接字的工作机制

1. 双向通信:

  • 在网络通信中,套接字充当两个应用程序之间的通信连接点。
  • 一个应用程序可以将数据写入其所在主机的套接字,数据通过网络接口卡(NIC)和传输介质发送到目标主机的套接字,从而实现数据传输。

2. 组合与寻址:

  • 套接字由 IP 地址和端口号组成,形成唯一的通信端点。
  • 这种组合提供了一种机制,使数据包能够准确地传输到目标应用进程。

套接字在操作系统中的表示

1. 套接字作为文件:

  • 在 Linux 环境下,套接字是一种特殊的文件类型,被内核用伪文件(pseudo-file)表示。
  • 由于套接字被视为文件,因此可以使用文件描述符来引用和操作套接字。
  • 这种设计使得套接字的读写操作与普通文件的读写操作一致,统一了接口。

2. 区别于管道:

  • 虽然套接字与管道(pipe)类似,都用于进程间通信,但套接字主要用于网络进程间的数据传递,而管道主要用于本地进程间的通信。

套接字的类型

1. 常见的套接字类型:

  • 流式套接字(Stream Socket):提供面向连接的、可靠的数据传输服务,典型的协议是 TCP。
  • 数据报套接字(Datagram Socket):提供无连接的、不可靠的数据传输服务,典型的协议是 UDP。
总结

套接字是网络编程中的核心概念,提供了在不同主机上的应用进程之间进行通信的机制。它通过 IP 地址和端口号进行唯一标识,并在操作系统中作为特殊的文件类型进行管理,使得网络通信操作与文件操作一致,从而简化了编程接口。

socket 本身有“插座”的意思,在 Linux 环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux 系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传 递。

image-20240522062732723

// 套接字通信分两部分:

- 服务器端:被动接受连接,一般不会主动发起连接

- 客户端:主动向服务器发起连接

socket是一套通信的接口,Linux 和 Windows 都有,但是有一些细微的差别。

字节序

字节序(Endian)问题是计算机系统中关于数据表示的重要概念,主要涉及到数据的存储和通信方式。在不同的计算机体系结构中,字节序可能不同,这就要求在进行数据传输和通信时必须明确字节序,以确保数据正确地被解释和处理。

大端字节序(Big-Endian)

大端字节序,也称为“网络字节序”,因为它是多数网络协议采用的字节序,包括TCP/IP。在大端字节序中,一个多字节数值的最高字节(即最重要的字节)被存储在最低的内存地址,其次是次高字节,依此类推。例如,一个32位整数0x12345678在内存中的存储方式如下(地址由低到高):

  • 地址 x + 0: 0x12
  • 地址 x + 1: 0x34
  • 地址 x + 2: 0x56
  • 地址 x + 3: 0x78
小端字节序(Little-Endian)

记忆:小低低

小端字节序则恰好相反,一个多字节数值的最低字节(即最不重要的字节)被存储在最低的内存地址。它被很多现代桌面计算机体系(如x86架构)采用。例如,同样的32位整数0x12345678在小端字节序的计算机中的存储方式如下:

  • 地址 x + 0: 0x78
  • 地址 x + 1: 0x56
  • 地址 x + 2: 0x34
  • 地址 x + 3: 0x12
字节序的重要性

在网络通信中,字节序的不一致会导致数据被错误解释,进而造成通信错误。因此,为了数据能在不同的计算机系统间正确传输和接收,往往需要在发送和接收端之间进行字节序的转换。例如,在一个小端字节序的机器上发送数据到一个大端字节序的机器上时,发送者需要将数据从小端转换为大端字节序。

在进行网络编程,尤其是跨平台通信时,理解并正确处理字节序问题是非常关键的。Socket编程中经常用到的htonl()ntohl()等函数就是用于在主机字节序和网络字节序之间转换整数的字节序。

总结来说,字节序是一个基础而重要的概念,涉及到计算机体系、操作系统平台、网络通信等多个层面,正确处理字节序问题对保证数据交换的正确性和效率至关重要。

image-20240522070409611

image-20240522070418150

案例
/*  
    字节序:字节在内存中存储的顺序。
    小端字节序:数据的高位字节存储在内存的高位地址,低位字节存储在内存的低位地址
    大端字节序:数据的低位字节存储在内存的高位地址,高位字节存储在内存的低位地址
*/

// 通过代码检测当前主机的字节序
#include <stdio.h>

int main() {

    union {
        short value;    // 2字节
        char bytes[sizeof(short)];  // char[2]
    } test;

    test.value = 0x0102;
    if((test.bytes[0] == 1) && (test.bytes[1] == 2)) {
        printf("大端字节序\n");
    } else if((test.bytes[0] == 2) && (test.bytes[1] == 1)) {
        printf("小端字节序\n");
    } else {
        printf("未知\n");
    }

    return 0;
}
daic@daic:~/Linux/linuxwebserver/part04networkProgramming$ gcc byteorder.c 
daic@daic:~/Linux/linuxwebserver/part04networkProgramming$ ./a.out 
小端字节序

在进行网络编程时,处理字节序是保证跨平台数据通信正确性的一个重要方面。你已经很好地概括了如何利用大端字节序(即网络字节序)来实现数据的一致性和正确传输。下面我们详细解释这些概念和具体的函数使用:

字节序转换函数

网络字节序的重要性

**网络字节序是大端(Big-Endian)字节序,**这意味着数据的高位字节存储在低地址上。TCP/IP 协议族规定使用大端字节序是为了确保数据在不同计算架构的机器间传输时,每一台设备都能以相同的方式解释这些数据。这个标准化的处理方式避免了每个应用程序需要考虑目标机器字节序的复杂性。

h - host 主机,主机字节序
to - 转换成什么
n - network 网络字节序
s - short unsigned short
l - long unsigned int
#include <arpa/inet.h>
// 转换端口
uint16_t htons(uint16_t hostshort); // 主机字节序 - 网络字节序
uint16_t ntohs(uint16_t netshort); // 主机字节序 - 网络字节序
// 转IP
uint32_t htonl(uint32_t hostlong); // 主机字节序 - 网络字节序
uint32_t ntohl(uint32_t netlong); // 主机字节序 - 网络字节序
字节序转换函数

BSD Socket API 提供了一些函数来帮助程序员在主机字节序和网络字节序之间转换数据。主要的函数有:

  • htonl() (Host TO Network Long): 将一个长整型数从主机字节序转换到网络字节序。
  • htons() (Host TO Network Short): 将一个短整型数从主机字节序转换到网络字节序。
  • ntohl() (Network TO Host Long): 将一个长整型数从网络字节序转换到主机字节序。
  • ntohs() (Network TO Host Short): 将一个短整型数从网络字节序转换到主机字节序。
使用场景
  • 发送数据时: 在发送数据之前,数据需要从主机字节序转换为网络字节序,使用 htonl()htons()
  • 接收数据时: 在读取从网络接收的数据时,需要将数据从网络字节序转换回主机字节序,使用 ntohl()ntohs()
示例

假设您正在编写一个跨平台的网络应用,您需要发送一个整数和一个短整型数:

#include <stdio.h>
#include <netinet/in.h>

int main() {
    int num = 0x12345678;
    short s_num = 0x1234;

    // 转换为网络字节序
    int net_num = htonl(num);
    short net_s_num = htons(s_num);

    // 发送 net_num 和 net_s_num ...
    
    // 接收数据
    // 假设接收到的数据存储在变量 net_num 和 net_s_num 中
    int host_num = ntohl(net_num);
    short host_s_num = ntohs(net_s_num);

    printf("Original: %x, Received: %x\n", num, host_num);
    printf("Original Short: %x, Received Short: %x\n", s_num, host_s_num);

    return 0;
}

这个例子展示了如何在发送和接收数据时处理字节序问题,确保数据在不同系统间正确地传输和解释。通过这种方式,无论数据是在小端还是大端机器上生成或接收的,都能正确处理,从而实现真正的跨平台兼容性。

案例
/*

    网络通信时,需要将主机字节序转换成网络字节序(大端),
    另外一段获取到数据以后根据情况将网络字节序转换成主机字节序。

    // 转换端口
    uint16_t htons(uint16_t hostshort);		// 主机字节序 - 网络字节序
    uint16_t ntohs(uint16_t netshort);		// 主机字节序 - 网络字节序

    // 转IP
    uint32_t htonl(uint32_t hostlong);		// 主机字节序 - 网络字节序
    uint32_t ntohl(uint32_t netlong);		// 主机字节序 - 网络字节序

*/

#include <stdio.h>
#include <arpa/inet.h>

int main() {

    // htons 转换端口
    // typedef unsigned short int __uint16_t;
    uint16_t a = 0x0102;
    printf("a : %x\n", a);
    unsigned short b = htons(a);
    printf("b : %x\n", b);

    printf("=======================\n");

    // htonl  转换IP
    char buf[4] = {192, 168, 1, 100};
    int num = *(int *)buf;
    int sum = htonl(num);
    unsigned char *p = (char *)&sum;

    printf("%d %d %d %d\n", *p, *(p+1), *(p+2), *(p+3));

    printf("=======================\n");

    // ntohl
    unsigned char buf1[4] = {1, 1, 168, 192};
    int num1 = *(int *)buf1;
    int sum1 = ntohl(num1);
    unsigned char *p1 = (unsigned char *)&sum1;
    printf("%d %d %d %d\n", *p1, *(p1+1), *(p1+2), *(p1+3));
    
     // ntohs


    return 0;
}
daic@daic:~/Linux/linuxwebserver/part04networkProgramming$ gcc bytetrans.c 
daic@daic:~/Linux/linuxwebserver/part04networkProgramming$ ./a.out 
a : 102
b : 201
=======================
100 1 168 192
=======================
192 168 1 1

socket地址

// socket地址其实是一个结构体,封装端口号和IP等信息。后面的socket相关的api中需要使用到这个
socket地址。
// 客户端 -> 服务器(IP, Port)
通用 socket 地址

socket 网络编程接口中表示 socket 地址的是结构体 sockaddr,其定义如下:

#include <bits/socket.h>
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
typedef unsigned short int sa_family_t;

sa_family 成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。常见的协议族(protocol family,也称 domain)和对应的地址族入下所示:

image-20240522080913698

sa_data 成员用于存放 socket 地址值。但是,不同的协议族的地址值具有不同的含义和长度,如下所示:

image-20240522081113172

由上表可知,14 字节的 sa_data 根本无法容纳多数协议族的地址值。因此,Linux 定义了下面这个新的通用的 socket 地址结构体,这个结构体不仅提供了足够大的空间用于存放地址值,而且是内存对齐的。

#include <bits/socket.h>
struct sockaddr_storage
{
sa_family_t sa_family;
unsigned long int __ss_align;
char __ss_padding[ 128 - sizeof(__ss_align) ];
};
typedef unsigned short int sa_family_t;
专用 socket 地址

很多网络编程函数诞生早于 IPv4 协议,那时候都使用的是 struct sockaddr 结构体,为了向前兼容,现 在sockaddr 退化成了(void *)的作用,传递一个地址给函数,至于这个函数是 sockaddr_in 还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型。

image-20240522104145742

UNIX 本地域协议族使用如下专用的 socket 地址结构体:

#include <sys/un.h>
struct sockaddr_un
{
sa_family_t sin_family;
char sun_path[108];
};

TCP/IP 协议族有 sockaddr_in 和 sockaddr_in6 两个专用的 socket 地址结构体,它们分别用于 IPv4 和

IPv6:

#include <netinet/in.h>
struct sockaddr_in
{
sa_family_t sin_family; /* __SOCKADDR_COMMON(sin_) */
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) - sizeof (struct in_addr)];
};


struct in_addr
{
in_addr_t s_addr;
};

struct sockaddr_in6
{
sa_family_t sin6_family;
in_port_t sin6_port; /* Transport layer port # */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* IPv6 scope-id */
};

typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))

所有专用 socket 地址(以及 sockaddr_storage)类型的变量在实际使用时都需要转化为通用 socket 地址类型 sockaddr(强制转化即可),因为所有 socket 编程接口使用的地址参数类型都是 sockaddr。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JS对象超细

目录 一、对象是什么 1.对象声明语法 2.对象有属性和方法组成 二、对象的使用 1.对象的使用 &#xff08;1&#xff09;查 &#xff08;2&#xff09;改 &#xff08;3&#xff09;增 &#xff08;4&#xff09;删&#xff08;了解&#xff09; &#xff08;5&#xf…

Lazarus - 从 Hello 开始

我们在《Lazarus - 隐秘的神器》一文中了解到了 Lazarus 的历史和特点&#xff0c;此后将正式开始学习Lazarus 开发。 如果你也对 Windows、Pascal、Delphi 开发感兴趣&#xff0c;请关注 Lazarus专栏 &#x1f4f0; 安装开发环境 官网&#xff1a;Lazarus Homepage (lazarus-i…

采用LoRA方法微调llama3大语言模型

文章目录 前言一、Llama3模型简介1.下载llama3源码到linux服务器2.安装依赖3.测试预训练模型Meta-Llama-3-8B4.测试指令微调模型Meta-Llama3-8B-Instruct5.小结 二、LoRA微调Llama31.引入库2.编写配置文件3.LoRA训练的产物 三、测试新模型效果1.编写配置文件2.运行配置文件&…

拼多多暂时超越阿里成为电商第一

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 拼多多的财报又炸裂了&#xff1a; 拼多多发布了第一季度财报&#xff0c;营收868亿&#xff0c;增长了131%&#xff0c;净利润279亿&#xff0c;增长了246%&#xff0c;营销服务收入424亿&#xff0c;也就是商家的…

如何将老板的游戏机接入阿里云自建K8S跑大模型(下)- 安装nvidia/gpu-operator支持GPU在容器中共享

文章目录 安装nvidia/gpu-operator支持GPU在容器中共享 安装nvidia/gpu-operator支持GPU在容器中共享 安装 nvidia/gpu-operator遇到两个问题&#xff1a; 由于我们都懂的某个原因&#xff0c;导致某些镜像一直现在不成功。 解决办法&#xff0c;准备一个&#x1fa9c;&#…

peakcan硬件配置-用于linux的socket_can通讯

1.相关系统环境 工控机型号&#xff1a;Nuvo-8108GC 系统版本&#xff1a;ubuntu 18 工控机内置can卡&#xff1a;peakcan 2.下载并安装peakcan驱动 下载链接1–下载链接2–peakcan8.15.2驱动&#xff0c;支持socketcan编程 2.1 安装依赖库 sudo apt-get install udev sud…

建模:Maya

一、常用按键 1、alt 左键 —— 环绕查看 2、alt 中键 —— 拖动模型所在面板 3、空格 —— 进入三视图模式&#xff1b;空格 左键按住拖动 —— 切换到对应视图 二、骨骼归零 1、T Pose 旋转模式&#xff0c;点击模型&#xff0c;摆好T姿势即可 2、复制模型设置200距离…

线程的概念和控制

文章目录 线程概念线程的优点线程的缺点线程异常线程用途理解虚拟地址 线程控制线程的创建线程终止线程等待线程分离封装线程库 线程概念 什么是线程&#xff1f; 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一…

嵩山是颍水的嵩山

颍水对于嵩山具有特别重要的意义&#xff0c;嵩山的水流大多数汇入了颍河&#xff0c;颍河流域约占登封市总面积88%&#xff0c;从这个角度讲&#xff0c;嵩山就是颍水的嵩山。 再看环嵩山地区&#xff0c;即“嵩山文化圈”&#xff0c;学者们按黄、淮、济分为三个水系区。黄河…

Vue3实战笔记(39)—封装页脚组件,附源码

文章目录 前言一、封装页脚组件二、使用组件总结 前言 在Web开发中&#xff0c;页脚组件是一个重要的部分&#xff0c;它为用户提供关于网站的信息、导航链接以及版权声明等。而封装页脚组件则是一种高效的方法&#xff0c;可以提高代码的可重用性和可维护性。 一、封装页脚组…

C++实现基于http协议的epoll非阻塞模型的web服务器框架(支持访问服务器目录下文件的解析)

使用方法&#xff1a; 编译 例子&#xff1a;./httpserver 9999 ../ htmltest/ 可执行文件 端口 要访问的目录下的 例子&#xff1a;http://192.168.88.130:9999/luffy.html 前提概要 http协议 &#xff1a;应用层协议&#xff0c;用于网络通信&#xff0c;封装要传输的数据&…

如何在Windows下使用Docker Desktop运行CentOS容器

引言&#xff1a; 在Windows操作系统中&#xff0c;我们可以使用Docker Desktop来轻松运行和管理各种Linux容器&#xff0c;包括CentOS。今天&#xff0c;我们就来详细讲解一下如何在Windows环境下使用Docker Desktop来运行CentOS容器。 一、安装Docker Desktop 首先&#x…

贴片反射式红外光电传感器ITR8307

红外光电传感器ITR8307 ITR8307外形 特性 快速响应时间 高灵敏度 非可见波长 薄 紧凑型 无铅 该产品本身将保持在符合RoHS的版本内 描述 ITR8307/S18/TR8是一种光反射开关&#xff0c;它包括一个GaAs IR-LED发射器和一个NPN光电晶体管&#xff0c;该晶体管具有短距离的高…

QGIS DEM数据快速获取

背景 Dem 是非常重要的数据&#xff0c;30 m 的精度也是最容易获取的&#xff0c;目前有很多种方式可以获取&#xff0c;比如地理空间数据云&#xff0c;今天介绍用 QGIS插件获取。 这种方式的最大优势是方便快捷。 插件下载与安装 插件-管理并安装插件-搜索下载 OpenTopogr…

5.23小结

1.java项目创新 目前想添加一个自动回复的功能和设置验证方式有&#xff08;允许任何人添加&#xff0c;禁止添加&#xff0c;设置回答问题添加&#xff0c;普通验证添加&#xff09; 目前只完成画好前端界面&#xff0c;前端发送请求&#xff0c;还有表的修改 因为涉及表字…

Baxter机器人摄像头打不开的一个可能的解决办法

操作过程 1.连上机器人 cd ros_ws/ ./baxter.sh2.查看摄像头&#xff08;最多开两个&#xff09; rosrun baxter_tools camera_control.py -l 3.打开指定的摄像头 rosrun baxter_tools camera_control.py -o left_hand_camera -r 1280x800 另&#xff1a;关闭的话 rosrun…

vscode安装多版本esp-idf

安装 离线安装 vscode设置 建立一个新的配置文件, 这里面的插件是全新的 安装esp-idf 官网下载espidf 安装这一个 选项默认即可 记住各一个路径, 之后要用到 vscode安装插件 安装以后会进入这一个界面, 也可以CtrlShiftP输入ESP-IDFextension进入 使用espressif 问题 这一个…

TreeMap及TreeSet详解

在介绍TreeMap和TraaSet之前我们先来介绍一下Map和Set这样便于大家后续理解。 有这张图我们可以看出Set是继承Collection而Map没有继承任何的类&#xff0c;了解这一点对于后续的学习 是比较有帮助的。 TreeMap和TreeSet实现的底层原理&#xff08;数据结构&#xff09;是相同的…

./scripts/Makefile.clean 文件分析

文章目录 目标 $(subdir-ymn)目标__clean $(clean-dirs):     make -f ./scripts/Makefile.clean obj$(patsubst _clean_%,%,$) $(clean-dirs)$(patsubst _clean_%,%,$)_clean_api _clean_cmd _clean_common _clean_disk _clean_drivers _clean_drivers/ddr/altera _clean_d…

设计模式12——外观模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 外观模式&#xff08;Facade&a…