蓝桉云顶

Good Luck To You!

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

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

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: 存储端口号(使用网络字节顺序)。

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位的短整数从网络字节序转换为主机字节序。

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地址和端口上。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

«    2024年12月    »
1
2345678
9101112131415
16171819202122
23242526272829
3031
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
文章归档
网站收藏
友情链接