Socket编程之sockaddr与sockaddr_in结构体

本文解析了tinyhttpd服务器端startup函数的工作原理,包括套接字创建、地址绑定、端口获取及监听设置等关键步骤,并详细解释了sockaddr_in与sockaddr结构体之间的关系及其在网络编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近学习tinyhttpd,在服务器端的startup函数中看到了如下声明以及调用:

struct sockaddr_in name;

bind(httpd, (struct sockaddr *)&name, sizeof(name);

getsockname(httpd, (struct sockaddr *)&name, &namelen;

以下是tinyhttpd中服务器端startup函数的代码:

/**********************************************************************/  
/* This function starts the process of listening for web connections 
* on a specified port.  If the port is 0, then dynamically allocate a 
* port and modify the original port variable to reflect the actual 
* port. 
* Parameters: pointer to variable containing the port to connect on 
* Returns: the socket */  
/**********************************************************************/  
/*服务器端套接字初始化设置*/  
int startup(u_short *port)  
{  
    int httpd = 0;  
    struct sockaddr_in name;  
  
    httpd = socket(PF_INET, SOCK_STREAM, 0);//创建服务器端套接字  
    if (httpd == -1)  
        error_die("socket");  
    memset(&name, 0, sizeof(name));  
    name.sin_family = AF_INET;//地址簇  
    name.sin_port = htons(*port);//指定端口  
    name.sin_addr.s_addr = htonl(INADDR_ANY);//通配地址  
    if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)//绑定到指定地址和端口,传入的sin_port为0,bind函数会随机给sin_port分配一个端口号,但是*port还是0 
        error_die("bind");  
    if (*port == 0)   
    {  
        int namelen = sizeof(name);  
        /*在以端口号0调用bind后,getsockname用于返回由内核赋予的本地端口号*/  
        if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)  
            error_die("getsockname");  
        *port = ntohs(name.sin_port);//网络字节顺序转换为主机字节顺序,返回主机字节顺序表达的数  
    }  
    if (listen(httpd, 5) < 0)//服务器监听客户端请求。套接字排队的最大连接个数5  
        error_die("listen");  
    return(httpd);  
}  
很奇怪为什么声明name的时候用的是sockadd_in结构体,而函数调用的时候传入的是sockaddr结构的name。于是查看sockadd_in和sockaddr的定义:
include <netinet/in.h>  
  
struct sockaddr {  
    unsigned short    sa_family;       // 2 bytes address family, AF_xxx  
    char              sa_data[14];     // 14 bytes of protocol address  
};  
  
// IPv4 AF_INET sockets:  
//sin_port和sin_addr都必须是网络字节序(NBO),一般可视化的数字都是主机字节序(HBO)
struct sockaddr_in {  
    short            sin_family;       // 2 bytes e.g. AF_INET, AF_INET6, 地址族
    unsigned short   sin_port;         // 2 bytes e.g. htons(3490)  TCP/UDP端口号
    struct in_addr   sin_addr;         // 4 bytes see struct in_addr, below  32位IP地址
    char             sin_zero[8];      // 8 bytes zero this if you want to  
};  

// 该结构体中提到的另一个结构体in_addr定义如下,它用来存放32位IP地址
struct in_addr {  
    unsigned long s_addr;          // 4 bytes load with inet_pton()  
}; 

struct sockaddr和struct sockaddr_in这两个结构体用来处理网络通信的地址。在各种系统调用或者函数中,只要和网络地址打交道,就得用到这两个结构体。

网络中的地址包含3个方面的属性:
1 地址类型: ipv4还是ipv6
2 ip地址

3 端口

那为什么声明name的时候用的是sockadd_in结构体,而函数调用的时候传入的是sockaddr结构的name?

sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了。sockaddr_in解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中。

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

通常把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数。

sockaddr_in用于socket定义和赋值
sockaddr用于函数参数

sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。 

sockaddr_in是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。

参考链接:

sockaddr和sockaddr_in的区别

[gcc编程] socket编程——sockaddr_in结构体操作


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值