热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C套接字sockaddr和sockaddr_storage背后的推理-ReasoningbehindCsocketssockaddrandsockaddr_storage

Imlookingatfunctionssuchasconnect()andbind()inCsocketsandnoticethattheytakeapoint

I'm looking at functions such as connect() and bind() in C sockets and notice that they take a pointer to a sockaddr struct. I've been reading and to make your application AF-Independent, it is useful to use the sockaddr_storage struct pointer and cast it to a sockaddr pointer because of all the extra space it has for larger addresses.

我将查看在C套接字中连接()和bind()之类的函数,并注意到它们使用一个指向sockaddr结构的指针。我一直在阅读并使您的应用程序与af无关,使用sockaddr_storage struct指针并将其转换为sockaddr指针是很有用的,因为它有足够的空间用于更大的地址。

What I am wondering is how functions like connect() and bind() that ask for a sockaddr pointer go about accessing the data from a pointer that points at a larger structure than the one it is expecting. Sure, you pass it the size of the structure you are providing it, but what is the actual syntax that the functions use to get the IP Address off the pointers to larger structures that you have cast to struct *sockaddr?

我想知道的是,像connect()和bind()这样请求sockaddr指针的函数是如何从指向比预期的更大结构的指针访问数据的。当然,您将提供的结构的大小传递给它,但是函数用于从指针中获取IP地址的实际语法是什么?

It's probably because I come from OOP languages, but it seems like kind of a hack and a bit messy.

这可能是因为我来自OOP语言,但它看起来有点像一个黑客,有点混乱。

2 个解决方案

#1


40  

Functions that expect a pointer to struct sockaddr probably typecast the pointer you send them to sockaddr when you send them a pointer to struct sockaddr_storage. In that way, they access it as if it was a struct sockaddr.

函数期望一个指向struct sockaddr的指针,当你向sockaddr发送一个指向struct sockaddr_storage的指针时,它可能会将你发送给sockaddr的指针进行排版。通过这种方式,他们访问它就好像它是一个结构体sockaddr。

struct sockaddr_storage is designed to fit in both a struct sockaddr_in and struct sockaddr_in6

struct sockaddr_storage被设计成适合于struct sockaddr_in和struct sockaddr_in6。

You don't create your own struct sockaddr, you usually create a struct sockaddr_in or a struct sockaddr_in6 depending on what IP version you're using. In order to avoid trying to know what IP version you will be using, you can use a struct sockaddr_storage which can hold either. This will in turn be typecasted to struct sockaddr by the connect(), bind(), etc functions and accessed that way.

您不会创建自己的struct sockaddr,您通常会创建一个struct sockaddr_in或struct sockaddr_in6,这取决于您使用的IP版本。为了避免尝试知道将要使用的IP版本,您可以使用一个struct sockaddr_storage,它可以保存其中任何一个。然后通过connect()、bind()等函数将其类型设置为struct sockaddr,并通过这种方式进行访问。

You can see all of these structs below (the padding is implementation specific, for alignment purposes):

您可以在下面看到所有这些结构(为对齐目的,填充是特定于实现的):

struct sockaddr {
   unsigned short    sa_family;    // address family, AF_xxx
   char              sa_data[14];  // 14 bytes of protocol address
};


struct sockaddr_in {
    short            sin_family;   // e.g. AF_INET, AF_INET6
    unsigned short   sin_port;     // e.g. htons(3490)
    struct in_addr   sin_addr;     // see struct in_addr, below
    char             sin_zero[8];  // zero this if you want to
};


struct sockaddr_in6 {
    u_int16_t       sin6_family;   // address family, AF_INET6
    u_int16_t       sin6_port;     // port number, Network Byte Order
    u_int32_t       sin6_flowinfo; // IPv6 flow information
    struct in6_addr sin6_addr;     // IPv6 address
    u_int32_t       sin6_scope_id; // Scope ID
};

struct sockaddr_storage {
    sa_family_t  ss_family;     // address family

    // all this is padding, implementation specific, ignore it:
    char      __ss_pad1[_SS_PAD1SIZE];
    int64_t   __ss_align;
    char      __ss_pad2[_SS_PAD2SIZE];
};

So as you can see, if the function expects an IPv4 address, it will just read the first 4 bytes (because it assumes the struct is of type struct sockaddr. Otherwise it will read the full 16 bytes for IPv6).

如您所见,如果函数需要一个IPv4地址,它将只读取前4个字节(因为它假设结构体是struct sockaddr类型。否则它将读取IPv6的全部16字节)。

#2


7  

In C++ classes with at least one virtual function are given a TAG. That tag allows you to dynamic_cast<>() to any of the classes your class derives from and vice versa. The TAG is what allows dynamic_cast<>() to work. More or less, this can be a number or a string...

在c++类中,具有至少一个虚函数的类被赋予一个标记。该标记允许您将dynamic_cast<>()转换为类派生的任何类,反之亦然。这个标记允许dynamic_cast<>()工作。或多或少,这可以是一个数字或一个字符串…

In C we are limited to structures. However, structures can also be assigned a TAG. In fact, if you look at all the structures that theprole posted in his answer, you will notice that they all start with 2 bytes (an unsigned short) which represents what we call the family of the address. This defines exactly what the structure is and thus its size, fields, etc.

在C语言中,我们仅限于结构。但是,也可以为结构分配一个标记。事实上,如果你看一下theprole在他的回答中发布的所有结构,你会发现它们都以2个字节(一个无符号的短)开头,这表示我们所说的地址的族。它准确地定义了结构是什么,因此它的大小、字段等等。

Therefore you can do something like this:

因此你可以这样做:

int bind(int fd, struct sockaddr *in, socklen_t len)
{
  switch(in->sa_family)
  {
  case AF_INET:
    if(len 

As you can see, the function can check the len parameter to make sure that the length is enough to fit the expected structure and therefore they can reinterpret_cast<>() (as it would be called in C++) your pointer. Whether the data is correct in the structure is up to the caller. There is not much choice on that end. These functions are expected to verify all sorts of things before it uses the data and return -1 and errno whenever a problem is found.

如您所见,该函数可以检查len参数,以确保长度足以满足预期的结构,因此它们可以重新解释您的指针(在c++中,它将被称为)的t_cast<>()。结构中的数据是否正确取决于调用者。在这方面没有多少选择。这些函数将在使用数据之前验证所有的东西,并在发现问题时返回-1和errno。

So in effect, you have a struct sockaddr_in or struct sockaddr_in6 that you (reinterpret) cast to a struct sockaddr and the bind() function (and others) cast that pointer back to a struct sockaddr_in or struct sockaddr_in6 after they checked the sa_family member and verified the size.

因此,实际上,您有一个struct sockaddr_in或struct sockaddr_in6,您(重新解释)将其转换为一个struct sockaddr, bind()函数(和其他函数)将指针转换回一个struct sockaddr_in或struct sockaddr_in6,在检查sa_family成员并验证大小之后。


推荐阅读
  • C语言编写线程池的简单实现方法
    2019独角兽企业重金招聘Python工程师标准好文章,一起分享——有时我们会需要大量线程来处理一些相互独立的任务,为了避免频繁的申请释放线程所带 ... [详细]
  • 传统上,Java 的 String 类一直使用 char 数组来存储字符数据。然而,在 Java 9 及更高版本中,String 类的内部实现改为使用 byte 数组。本文将探讨这一变化的原因及其带来的好处。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • 如何使用 `org.opencb.opencga.core.results.VariantQueryResult.getSource()` 方法及其代码示例详解 ... [详细]
  • 小程序的授权和登陆
    小程序的授权和登陆 ... [详细]
  • 一、MATLAB常用的基本数学函数abs(x):纯量的绝对值或向量的长度angle(z):复数z的相角(Phaseangle)sqrt(x)࿱ ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • 【妙】bug称它为数组越界的妙用
    1、聊一聊首先跟大家推荐一首非常温柔的歌曲,跑步的常听。本文主要把自己对C语言中柔性数组、零数组等等的理解分享给大家,并聊聊如何构建一种统一化的学习思想 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 在C#编程中,数值结果的格式化展示是提高代码可读性和用户体验的重要手段。本文探讨了多种格式化方法和技巧,如使用格式说明符、自定义格式字符串等,以实现对数值结果的精确控制。通过实例演示,展示了如何灵活运用这些技术来满足不同的展示需求。 ... [详细]
  • 在Android开发中,BroadcastReceiver(广播接收器)是一个重要的组件,广泛应用于多种场景。本文将深入解析BroadcastReceiver的工作原理、应用场景及其具体实现方法,帮助开发者更好地理解和使用这一组件。通过实例分析,文章详细探讨了静态广播的注册方式、生命周期管理以及常见问题的解决策略,为开发者提供全面的技术指导。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有