【Linux】socket套接字

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析

在这里插入图片描述


👉🏻IP地址和端口号

IP地址和端口号是网络通信中重要的概念,它们用于标识网络中的设备和应用程序。这两者结合在一起,可以唯一确定网络中的一个进程,从而实现进程间的通信。

  1. IP地址(Internet Protocol Address):IP地址是网络上设备的标识符,用于在网络中唯一标识一个设备(如计算机、路由器等)。IP地址分为IPv4和IPv6两种格式。IPv4地址通常由四个用点分隔的十进制数组成(例如,192.168.1.1),而IPv6地址则更长,由八组十六进制数构成(例如,2001:0db8:85a3:0000:0000:8a2e:0370:7334)。在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址

  2. 端口号(Port Number):端口号是一个逻辑地址,用于标识一个特定的进程或服务。在通信中,每个数据包都会标记源端口和目标端口。端口号的范围是从0到65535,其中0到1023是被系统保留的一些常用端口,用于一些特定的服务(比如HTTP的端口80,HTTPS的端口443等)。端口号是一个2字节16位的整数;一个端口号只能被一个进程占用

一个进程可以绑定多个端口号; 但是一个端口号不能被多个进程绑定

IP地址和端口号结合在一起,可以唯一地标识网络中的一个进程。例如,一个Web服务器可能会使用IP地址192.168.1.1和端口号80来提供HTTP服务。当客户端想要访问这个Web服务器时,它会使用服务器的IP地址和端口号来建立连接并发送请求。服务器收到请求后,会通过端口号将请求交给相应的Web服务进程来处理,并将响应返回给客户端。

pid和port的关系

  1. PID(进程标识符)

    • PID是操作系统给每个正在运行的进程分配的唯一标识符。
    • PID在系统范围内是唯一的,它用于操作系统在管理和跟踪进程时进行标识。
    • PID通常是一个整数值,其范围取决于操作系统。在大多数Unix-like系统中,PID从1开始递增,直到达到系统所能分配的最大值。
  2. 端口号

    • 端口号是用于标识正在运行的网络服务或应用程序的数字标识符。
    • 端口号是网络通信中的概念,在进程间通信(IPC)中通常不涉及。
    • 端口号的范围是从0到65535,其中0到1023是系统保留端口,用于一些常见的网络服务,如HTTP(端口80)和HTTPS(端口443)。

虽然PID和端口号都用于标识进程或服务,但它们在不同的上下文中发挥作用。PID是操作系统级别的标识符,用于管理和跟踪进程,而端口号是网络通信中用于标识正在运行的网络服务或应用程序的标识符

总的来说就是port的主要作用就是将操作系统中的进程管理与网络解耦,port用来专门进行网络通信的,在网络中进行进程标识,虽然二者都是标识进程的id,但是作用域不同,所以也不冲突。

👉🏻TCP和UDP

  1. TCP(Transmission Control Protocol)传输控制协议

    • TCP 是一种面向连接的、可靠的基于字节流的传输协议。
    • TCP 提供了可靠的数据传输,通过确认机制重传机制流量控制来确保数据的可靠性和顺序性。
    • TCP 在通信开始前需要建立连接(三次握手),然后在通信结束后进行连接的断开(四次挥手)。
    • TCP 适用于需要可靠数据传输和顺序传输的应用场景,如网页浏览、文件传输等。
  2. UDP(User Datagram Protocol)用户数据报协议

    • UDP 是一种简单的无连接的不可靠的传输协议。
    • UDP 不提供数据传输的可靠性和顺序性,数据包可能会丢失、重复或乱序到达
    • UDP 的优点是速度快开销小,适用于一些实时性要求高、数据丢失可容忍的应用场景。
    • UDP 不需要建立连接,通信双方直接发送数据包,因此在通信开始和结束时没有连接建立和断开的过程。

总的来说,TCP 提供了可靠的数据传输服务,适用于需要数据完整性和顺序性的场景;而 UDP 则提供了更快的传输速度,适用于一些实时性要求高、对数据丢失不敏感的场景,如音视频传输、在线游戏等。选择使用哪种协议取决于应用的需求和对数据传输特性的要求。

👉🏻网络字节序

在C语言的学习的时候,我们学习过了大小端的知识,大端就是将数据的高位字节存储在内存地址中的低位地址,小端则反之。
由于每个机器采用的大小端都不一样,所以容易导致数据传输的字节顺序会出问题。
所以网络规定:所有到达网络的数据,必须是大端,所有从网络收到数据的机器,都会知道数据是大端(之所以采取大端,是因为可读性比较高)

网络字节序是一种规范,用于在网络上跨越不同体系结构的计算机之间进行数据通信时统一数据的字节顺序。它定义了在网络传输中使用的标准字节顺序,以确保数据在发送和接收端正确解释。

在网络字节序中,数据被以大端序(Big-Endian)的方式传输,即最高有效字节在前,最低有效字节在后。这与许多计算机体系结构的本地字节序(如x86架构的小端序)可能不同。

网络字节序通常用于以下方面:

  1. 网络通信协议:许多网络通信协议(如TCP/IP、UDP、HTTP)都要求在网络上传输数据时使用网络字节序,以确保数据在不同计算机上的解释一致。

  2. 网络编程:在进行网络编程时,开发人员通常需要手动将数据从本地字节序转换为网络字节序,以便正确发送到网络上。类似地,在接收数据时,也需要将数据从网络字节序转换为本地字节序。

  3. 数据存储:有时候,数据需要以网络字节序的形式存储到文件或数据库中,以便后续在网络上传输或从网络接收时能够正确解释。


为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络
字节序和主机字节序的转换。
🌨这些函数通常用于在网络编程中进行字节序的转换,以确保在不同计算机之间正确地传输数据。这些函数包括:

  1. htonl(host to network long):

    • 函数原型uint32_t htonl(uint32_t hostlong)
    • 作用:将32位无符号整数从主机字节序转换为网络字节序(大端序)。
    • 参数意义hostlong是要转换的32位整数。
    • 返回值:返回转换后的网络字节序的32位无符号整数。
  2. htons(host to network short):

    • 函数原型uint16_t htons(uint16_t hostshort)
    • 作用:将16位无符号短整数从主机字节序转换为网络字节序(大端序)。
    • 参数意义hostshort是要转换的16位短整数。
    • 返回值:返回转换后的网络字节序的16位无符号短整数。
  3. ntohl(network to host long):

    • 函数原型uint32_t ntohl(uint32_t netlong)
    • 作用:将32位无符号整数从网络字节序(大端序)转换为主机字节序。
    • 参数意义netlong是要转换的32位整数。
    • 返回值:返回转换后的主机字节序的32位无符号整数。
  4. ntohs(network to host short):

    • 函数原型uint16_t ntohs(uint16_t netshort)
    • 作用:将16位无符号短整数从网络字节序(大端序)转换为主机字节序。
    • 参数意义netshort是要转换的16位短整数。
    • 返回值:返回转换后的主机字节序的16位无符号短整数。
  • 这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回 ;
  • 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回。

这些函数通常用于网络编程中,以便在发送和接收数据时正确地处理字节序,确保数据在不同主机之间的传输和解释一致。在大多数情况下,主机字节序是指本地计算机的字节序,而网络字节序是指大端序

👉🏻socket网络编程以及接口函数

socket概念

Socket(套接字)是在网络编程中用于实现网络通信的一种机制。它提供了一种在不同主机之间进行双向通信的方式,使得客户端和服务器之间可以进行数据交换。

具体来说,Socket是一种通信端点,可以用于在两个计算机之间建立连接并进行数据传输。它可以看作是一种抽象接口,用于通过网络在进程之间传递数据。在网络编程中,Socket通常通过套接字地址(包括IP地址和端口号)来标识。套接字地址用于唯一地标识网络上的每个通信端点。

🌈Socket通常分为两种类型:

  1. 流套接字(Stream Socket):也称为TCP套接字,提供面向连接的、可靠的、双向的字节流通信。在使用流套接字时,通信双方必须先建立连接,然后才能进行数据传输。TCP协议就是基于流套接字实现的。

  2. 数据报套接字(Datagram Socket):也称为UDP套接字,提供无连接的、不可靠的数据报通信。在使用数据报套接字时,通信双方无需建立连接,每个数据报都是独立的,可能会丢失或重复。UDP协议就是基于数据报套接字实现的。

🌈Socket编程通常涉及以下步骤:

  • 创建Socket:在编程中创建一个套接字对象,以便于后续的通信。
  • 绑定地址:将套接字绑定到一个特定的地址和端口上,以便其他计算机可以找到并与之通信。
  • 监听连接(仅针对服务器端):对于服务器端程序,需要开始监听传入的连接请求。
  • 建立连接(仅针对客户端):对于客户端程序,需要与服务器端建立连接。
  • 数据传输:通过套接字进行数据的发送和接收。
  • 关闭连接:通信完成后,关闭套接字以释放资源。

socket

Socket函数是用于创建套接字的系统调用,在网络编程中非常重要。具体来说,Socket函数用于创建一个套接字对象,以便后续的通信。

以下是Socket函数的一般信息:

  • 函数原型

    int socket(int domain, int type, int protocol);
    
  • 作用:创建一个套接字对象,用于后续的网络通信。

  • 参数意义

    • domain:指定套接字的协议族,常用的有以下几种:

      • AF_INET:IPv4协议族
      • AF_INET6:IPv6协议族
      • AF_UNIX:Unix域协议族
      • 等等,还有其他协议族
    • type:指定套接字的类型,常用的有以下几种:

      • SOCK_STREAM:流套接字,提供面向连接的、可靠的、双向的字节流通信(如TCP套接字)。
      • SOCK_DGRAM:数据报套接字,提供无连接的、不可靠的数据报通信(如UDP套接字)。
      • SOCK_RAW:原始套接字,用于直接访问网络层,通常需要特权。
      • 等等,还有其他类型的套接字
    • protocol:指定套接字所使用的协议,通常为0表示由系统根据domaintype参数自动选择合适的协议。例如,在IPv4下,TCP协议的值为IPPROTO_TCP,UDP协议的值为IPPROTO_UDP

  • 返回值:若成功创建套接字,则返回一个新的套接字描述符(非负整数),用于后续的操作;若失败,则返回-1,并设置全局变量errno表示错误类型。

在使用Socket函数创建套接字后,通常需要根据具体的网络通信需求,调用其他函数来设置套接字选项、绑定地址、建立连接等操作,从而实现完整的网络通信功能。

bind

bind函数用于将套接字与特定的地址(IP地址和端口号)绑定在一起,以便其他计算机可以找到并与之通信。具体来说,bind函数在服务器端程序中通常用于指定服务器的监听地址和端口。

以下是bind函数的一般信息:

  • 函数原型

    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  • 作用将套接字与特定的地址(IP地址和端口号)绑定在一起,以便进行通信,说的清晰点,就是bind之后,我们的主机在网络中获得了属于自己的ip地址和端口号,作为自己的标识记号

  • 参数意义

    • sockfd:表示要绑定的套接字描述符,即通过socket函数创建的套接字对象。

    • addr:指向存储地址信息的结构体的指针,通常是sockaddr结构体或其派生结构体(如sockaddr_in结构体),用于指定要绑定的地址信息

    • addrlen:表示存储在addr指针中的地址结构体的长度。

  • 返回值:若成功绑定地址,则返回0;若失败,则返回-1,并设置全局变量errno表示错误类型。

在调用bind函数之前,通常需要先创建套接字对象,并设置好地址信息。成功绑定地址后,服务器端就可以通过监听该地址来接受客户端的连接请求,从而建立通信连接。需要注意的是,同一时间内,一个端口只能被一个套接字绑定,否则会导致绑定失败。

std::bind

std::bind 是 C++ 标准库 <functional> 头文件中的一个函数模板,用于创建函数对象(也称为绑定器或者函数适配器)。它可以将函数和其所需的参数绑定在一起,生成一个新的可调用对象。

std::bind 的基本语法如下:

#include <functional>

auto new_function = std::bind(function, args...);

其中:

  • function 是一个可调用对象,可以是函数指针、函数对象、成员函数指针或者函数对象指针。
  • args... 是要绑定到函数的参数列表。

std::bind 返回一个可调用对象 new_function,它绑定了指定函数和参数,可以在稍后的时间调用。

除了指定函数和参数外,std::bind 还可以指定参数的占位符。占位符使用 std::placeholders::_N 标识,其中 N 是参数的位置。例如,_1 表示第一个参数,_2 表示第二个参数,依此类推。

下面是一个简单的示例,演示了如何使用 std::bind

#include <iostream>
#include <functional>

// 定义一个简单的函数
void print_sum(int a, int b) {
    std::cout << a + b << std::endl;
}

int main() {
    // 使用 std::bind 绑定函数和参数
    auto new_function = std::bind(print_sum, 10, std::placeholders::_1);

    // 调用绑定后的函数,传入一个参数
    new_function(20); // 输出:30

    return 0;
}

在这个示例中,我们定义了一个简单的函数 print_sum,然后使用 std::bind 绑定了这个函数和一个常量参数 10,同时使用占位符 _1 表示函数的第二个参数。最后,我们调用了绑定后的函数 new_function,传入了一个参数 20,最终输出了 10 + 20 的结果 30。

listen

listen函数用于将一个套接字设置为被动监听状态,使其能够接受连接请求。通常在服务器端程序中,调用listen函数来准备接受客户端的连接请求

以下是listen函数的一般信息:

  • 函数原型

    int listen(int sockfd, int backlog);
    
  • 作用:将一个套接字设置为被动监听状态,以便接受连接请求。

  • 参数意义

    • sockfd:表示要设置监听状态的套接字描述符,即通过socket函数创建的套接字对象。

    • backlog:表示在内核中允许的连接请求的最大排队数量。当有新的连接请求到达时,如果等待队列已满,则后续的连接请求将被拒绝。注意,该参数不同操作系统的取值范围可能有所不同。

  • 返回值:若成功设置监听状态,则返回0;若失败,则返回-1,并设置全局变量errno表示错误类型。

调用listen函数之后,套接字将进入被动监听状态,开始等待客户端的连接请求。一旦有新的连接请求到达,并且内核等待队列未满,那么服务器端就可以通过accept函数接受这些连接请求,并建立通信连接。需要注意的是,调用listen函数并不会阻塞程序的执行,而是将套接字设置为监听状态后立即返回。

accept

accept函数用于接受客户端的连接请求,并创建一个新的套接字与客户端进行通信。通常在服务器端程序中,调用accept函数来处理客户端的连接请求,并建立通信连接。

以下是accept函数的一般信息:

  • 函数原型

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
  • 作用:接受客户端的连接请求,并创建一个新的套接字与客户端进行通信

  • 参数意义

    • sockfd表示已经处于监听状态的套接字描述符,即通过socket函数创建的套接字对象。

    • addr:用于存储客户端地址信息的结构体指针。当成功接受客户端连接后,该指针所指向的结构体将被填充为客户端的地址信息。

    • addrlen:表示存储在addr指针中的地址结构体的长度。在调用accept函数之前,需要将其设置为可修改的地址结构体的长度。

  • 返回值:若成功接受连接请求,则返回一个新的套接字描述符,用于与客户端进行通信;若失败,则返回-1,并设置全局变量errno表示错误类型。

调用accept函数之后,服务器端程序将会阻塞,直到有新的客户端连接请求到达。一旦有客户端连接成功,accept函数将返回一个新的套接字描述符,服务器可以使用这个新的套接字与客户端进行通信。同时,accept函数还会将客户端的地址信息存储在提供的addr结构体中,方便服务器端获取客户端的地址和端口信息。

accept函数,为什么后续服务端要用accept函数返回来的新套接字与客户端进行通信,而不是服务端原本的套接字?就比如recvfrom函数和sendto函数,都是共用同一个套接字,accept为什么如此不同? 🤔
原因如下:

想象一下,你是一家披萨店的老板,服务端就像是你的店铺,而客户端就像是顾客。

现在有一个很长的排队队伍,每个人都想要订购你的披萨。你(服务端)的工作是处理他们的订单。

现在,你有两种选择来处理这些订单:

  1. 直接处理:每当有人排队时,你直接从披萨店的柜台拿出一份披萨给他们,然后继续等待下一个人。

  2. 雇佣员工来处理:你雇佣了一群员工来处理订单。每次有人排队时,你指派一个员工去做披萨,然后将披萨交给顾客。

这里的关键区别是:在第一种情况下,你(服务端)直接处理订单(通过自己的套接字),而在第二种情况下,你(服务端)将订单委托给员工(新的套接字),然后继续等待下一个顾客。

这就是为什么服务端使用 accept 函数返回来的新套接字与客户端进行通信,而不是直接使用服务端原本的套接字。通过 accept 函数创建的新套接字相当于雇佣了一个员工来处理特定的客户端,这样可以使服务端能够同时处理多个客户端的请求,就像披萨店同时服务多个顾客一样。

connect

connect函数用于建立与远程服务器的连接。通常在客户端程序中,调用connect函数来连接服务器,以便进行数据通信。

以下是connect函数的一般信息:

  • 函数原型

    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  • 作用:建立与远程服务器的连接。

  • 参数意义

    • sockfd:表示客户端本地套接字的描述符,即通过socket函数创建的套接字对象。

    • addr:指向远程服务器地址信息的结构体指针,通常是通过调用getaddrinfo或手动构建的。

    • addrlen:表示提供给addr参数的地址结构体的长度。

  • 返回值:若连接成功,则返回0;若失败,则返回-1,并设置全局变量errno表示错误类型。

调用connect函数后,客户端会尝试与指定的服务器建立连接。如果连接成功,那么客户端就可以通过已连接的套接字进行数据通信。如果连接失败,则通常是由于服务器不可达、端口未开放或网络问题等原因。在连接函数返回之前,通常会阻塞程序的执行,直到连接建立成功或失败。

setsockopt

setsockopt是一个用于设置套接字选项的函数。它允许在套接字级别(如SOL_SOCKET)或其他协议级别(如IPPROTO_TCP、IPPROTO_IP等)上设置特定的选项。这些选项控制套接字的行为和特性。

函数的原型通常如下:

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

参数说明:

  • sockfd:标识一个套接字的描述字。
  • level:被设置的选项的级别。常见的级别包括SOL_SOCKET(套接字级别选项)、IPPROTO_TCP(TCP协议选项)、IPPROTO_IP(IP协议选项)等。
  • optname:需设置的选项的名称。具体的选项取决于所选择的级别。
  • optval:指向存放选项待设置的新值的缓冲区。
  • optlenoptval缓冲区的长度。

如果函数成功执行,它将返回0;否则,它将返回SOCKET_ERROR,并可以通过调用WSAGetLastError()来获取相应的错误代码。

例如,如果你想要设置套接字以便它可以进行广播,你可以使用以下代码:

int on = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {
    perror("fail to setsockopt");
    exit(1);
}

在这个例子中,sockfd是套接字的描述字,SOL_SOCKET表示我们想要设置的是套接字级别的选项,SO_BROADCAST是具体的选项名称,表示允许广播,&on是指向新值(在这个例子中是1,表示启用广播)的指针,sizeof(on)是新值的长度。

int opt = 1;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

通过设置SO_REUSEADDR选项,你可以避免TIME_WAIT状态带来的延迟,使得你的服务器程序能够更快地重启并重新监听相同的端口。但是,请注意,这可能会带来一些风险,因为旧的数据包仍然可能会到达并影响新的连接。因此,在使用此选项时,你需要确保了解相关的潜在问题,并采取适当的措施来避免它们。

运用socker接口函数实现服务端和客户端的实例代码

服务器端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define PORT 8080
#define BACKLOG 5
#define BUFFER_SIZE 1024

int main() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};
    const char *hello = "Hello from server";

    // 创建套接字
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 设置套接字选项,允许地址重用
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // 将套接字绑定到指定的端口
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 开始监听连接请求
    if (listen(server_fd, BACKLOG) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // 等待并接受客户端连接
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
        perror("accept");
        exit(EXIT_FAILURE);
    }

    // 从客户端接收数据
    read(new_socket, buffer, BUFFER_SIZE);
    printf("Client: %s\n", buffer);

    // 向客户端发送数据
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");

    return 0;
}

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};
    const char *hello = "Hello from client";

    // 创建套接字
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // 将IPv4地址从文本转换为二进制形式
    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    }

    // 向服务器发送数据
    send(sock, hello, strlen(hello), 0);
    printf("Hello message sent\n");

    // 从服务器接收数据
    read(sock, buffer, BUFFER_SIZE);
    printf("Server: %s\n",buffer);

    return 0;
}

这个例子中,服务器端创建了一个套接字,绑定到指定的端口,并监听连接请求。当客户端连接到服务器时,服务器接受连接并接收来自客户端的消息,然后向客户端发送一条消息。客户端连接到服务器,向服务器发送一条消息,并接收来自服务器的响应。

👉🏻sockaddr结构

struct sockaddr

struct sockaddr 是一个通用的套接字地址结构体,用于表示套接字的地址信息。在网络编程中,它通常被用于指定套接字的地址,包括 IP 地址和端口号。这个结构体是用于支持不同协议族的通用性。

下面是 struct sockaddr 的定义:

struct sockaddr {
    unsigned short sa_family;   // 地址族(Address Family),如 AF_INET(IPv4)或 AF_INET6(IPv6)
    char sa_data[14];           // 地址数据,包含具体的地址信息
};

由于 struct sockaddr 是一个通用的结构体,它的大小固定为 16 字节。因此,对于具体的地址族(如 IPv4 或 IPv6),通常会使用它的变体,如 struct sockaddr_in(IPv4)或 struct sockaddr_in6(IPv6),它们在 struct sockaddr 的基础上添加了一些额外的字段来表示特定地址族的地址信息。

在实际使用中,通常不直接使用 struct sockaddr,而是使用具体的地址结构体,如 struct sockaddr_instruct sockaddr_in6,因为它们提供了更多的字段来存储地址信息,并且更方便使用。

struct sockaddr_in

struct sockaddr_in 是用于表示 IPv4 地址的套接字地址结构体,在网络编程中经常使用。它包含在 <netinet/in.h> 头文件中。

下面是 struct sockaddr_in 的定义:

struct sockaddr_in {
    short int sin_family;           // 地址族(Address Family),通常为 AF_INET
    unsigned short int sin_port;    // 16 位端口号,网络字节序(big-endian)
    struct in_addr sin_addr;        // IPv4 地址结构体
    unsigned char sin_zero[8];      // 用于填充,使 sockaddr_in 和 sockaddr 的大小相同
};

其中:

  • sin_family:表示地址族,通常设置为 AF_INET,表示 IPv4 地址族。
  • sin_port:表示端口号,是一个 16 位的整数,以网络字节序(big-endian)存储。
  • sin_addr:是一个 struct in_addr 结构体,用于存储 IPv4 地址信息
  • sin_zero:用于填充,使 struct sockaddr_instruct sockaddr 的大小相同,总共占据 8 个字节。

struct in_addr 的定义如下:

struct in_addr {
    uint32_t s_addr;    // IPv4 地址,32 位无符号整数,以网络字节序存储
};

struct sockaddr_in 通常用于存储和表示 IPv4 地址信息,例如在创建套接字、绑定地址和连接远程主机时使用。使用这个结构体可以方便地操作和管理 IPv4 地址


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值