探索sockaddr,网络编程中的地址结构解析

sockaddr 是 C 语言中用于表示网络地址的结构体,包括 IP 地址和端口号等信息。

sockaddr详解

sockaddr

sockaddr结构体是网络编程中用于表示套接字地址的一种通用数据结构,它主要用于存储参与(IP)Windows/Linux套接字通信的计算机上的一个互联网协议(IP)地址,通过统一地址结构的表示方法,使得不同的地址结构可以被bind()、connect()、recvfrom()、sendto()等函数调用。

sockaddr结构体定义

sockaddr结构体在头文件#include <sys/socket.h>中定义,其定义如下:

struct sockaddr {
    sa_family_t sa_family; // 地址族,AF_xxx
    char        sa_data[14]; // 14字节的协议地址
};

sa_family: 2字节的地址家族,一般都是“AF_xxx”的形式,它的值包括三种:AF_INET(IPv4)、AF_INET6(IPv6)和AF_UNSPEC,如果指定AF_INET,那么函数就不能返回任何IPV6相关的地址信息;如果仅指定了AF_INET6,则就不能返回任何IPv4地址信息。AF_UNSPEC意味着函数返回的是适用于指定主机名和服务名且适合任何协议族的地址。

sa_data: 14字节的协议地址,包含套接字中的目标地址和端口信息。

sockaddr_in结构体定义

为了解决sockaddr结构体的缺陷,引入了sockaddr_in结构体,它在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义,其定义如下:

struct sockaddr_in {
    short            sin_family; // 地址族,AF_INET
    unsigned short   sin_port;   // 端口号,网络字节序
    struct in_addr   sin_addr;   // IPv4地址
    char             sin_zero[8]; // 填充字节,使结构体大小与sockaddr一致
};

sin_family: 指代协议族,在socket编程中只能是AF_INET

sin_port: 存储端口号(使用网络字节顺序)。

sockaddr

sin_addr: 存储IP地址,使用in_addr这个数据结构。

sin_zero: 是为了让sockaddrsockaddr_in两个数据结构保持大小相同而保留的空字节。

其中in_addr结构的定义如下:

typedef struct in_addr {
    union {
        struct {unsigned char s_b1,s_b2,s_b3,s_b4;} S_un_b;
        struct {unsigned short s_w1,s_w2;} S_un_w;
        unsigned long      S_addr;     // load with zero
    } S_un;
    #define s_addr     S_un.S_addr    // can be used for most tcp/ip implementations
    #define s_host     ((unsigned char*)&S_un)
    #define s_aliases  s_aliases
    #define s_ni_addr  s_addr
    #define s_nlen     4
    #define s_maxlenipp 4
};

网络字节序与主机字节序

在网络通信中,由于不同计算机可能采用不同的字节序(大端或小端),因此需要进行字节序的转换,TCP/IP协议规定:网络数据流应采用大端字节序(低地址高字节序),所以如果当前发送主机方是小端,需要先将数据转成大端,常用的转换函数有:

htonl: 将32位的长整数从主机字节序转换为网络字节序。

htons: 将16位的短整数从主机字节序转换为网络字节序。

ntohl: 将32位的长整数从网络字节序转换为主机字节序。

ntohs: 将16位的短整数从网络字节序转换为主机字节序。

sockaddr

sockaddrsockaddr_in的区别

sockaddr是一个通用的套接字地址结构,用于兼容IPv4和IPv6,但在实际编程中并不直接对此数据结构进行操作,而是使用另一个与之等价的数据结构sockaddr_in

sockaddr_in专门用于IPv4网络通信,解决了sockaddr结构体中目标地址和端口信息混在一起的问题,把端口和地址分开储存在两个变量中。

二者长度一样,都是16个字节,占用的内存大小是一致的,因此可以互相转化,指向sockaddr_in结构的指针也可以指向sockaddr

使用示例

以下是一个简单的使用示例,展示如何使用sockaddr_in结构体来绑定一个套接字到特定的IP地址和端口上:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main() {
    int sockfd;
    struct sockaddr_in mysock;
    // 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    // 清零并设置sockaddr_in结构体
    memset(&mysock, 0, sizeof(mysock));
    mysock.sin_family = AF_INET;
    mysock.sin_port = htons(8080); // 设置端口号为8080,网络字节序
    mysock.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置IP地址为127.0.0.1(localhost)
    // 绑定套接字到指定的IP地址和端口上
    if (bind(sockfd, (struct sockaddr *)&mysock, sizeof(mysock)) < 0) {
        perror("bind");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("Socket bound to 127.0.0.1:8080
");
    // 关闭套接字
    close(sockfd);
    return 0;
}

在这个示例中,首先创建了一个套接字,然后清零并设置了sockaddr_in结构体的各项参数,最后将该结构体强制转换为sockaddr类型并传递给bind()函数以绑定套接字到指定的IP地址和端口上。

原创文章,作者:未希,如若转载,请注明出处:https://www.kdun.com/ask/1387956.html

本网站发布或转载的文章及图片均来自网络,其原创性以及文中表达的观点和判断不代表本网站。如有问题,请联系客服处理。

(0)
未希的头像未希新媒体运营
上一篇 2024-12-07 13:48
下一篇 2024-12-07 13:50

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

产品购买 QQ咨询 微信咨询 SEO优化
分享本页
返回顶部
云产品限时秒杀。精选云产品高防服务器,20M大带宽限量抢购 >>点击进入