Android C++ 系列:Linux Socket 编程(一)预备知识

简介: 为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运 行,可以调用以下库函数做网络字节序和主机字节序的转换。

image.png


1. 网络字节序


我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。网络数据流同样有大端小端之分,那么如何定义网络数据流的地址呢?发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存,因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。


TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。例如前面文章介绍的的UDP 段格式,地址0-1是16位的源端口号,如果这个端口号是1000(0x3e8),则地址0是0x03, 地址1是0xe8,也就是先发0x03,再发0xe8,这16位在发送主机的缓冲区中也应该是低地址 存0x03,高地址存0xe8。但是,如果发送主机是小端字节序的,这16位被解释成0xe803,而 不是1000。因此,发送主机把1000填到发送缓冲区之前需要做字节序的转换。同样地,接收 主机如果是小端字节序的,接到16位的源端口号也要做字节序的转换。如果主机是大端字节 序的,发送和接收都不需要做转换。同理,32位的IP地址也要考虑网络字节序和主机字节序 的问题。


为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运 行,可以调用以下库函数做网络字节序和主机字节序的转换。


#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong); 
uint16_t htons(uint16_t hostshort); 
uint32_t ntohl(uint32_t netlong); 
uint16_t ntohs(uint16_t netshort);


h表示host,n表示network,l表示32位长整数,s表示16位短整数。 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。


2. IP地址转换函数


早期:


#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp); 
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
只能处理IPv4的ip地址 不可重入函数 注意参数是struct in_addr


最新:


#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
支持IPv4和IPv6 可重入函数


其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr, 因此函数接口是void *addrptr。


3. sockaddr数据结构


strcut sockaddr 很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结 构体,为了向前兼容,现在sockaddr退化成了(void *)的作用,传递一个地址给函数,至 于这个函数是sockaddr_in还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转 化为所需的地址类型


image.png


struct sockaddr { 
  sa_family_t sa_family;/* address family, AF_xxx */
  char sa_data[14]; /* 14 bytes of protocol address */
};
struct sockaddr_in { 
  __kernel_sa_family_t sin_family; /* Address family */
  __be16 sin_port; /* Port number */
  struct in_addr sin_addr; /* Internet address */
  /* Pad to size of `struct sockaddr'. */
  unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];
};
/* Internet address. */ 
struct in_addr {
  __be32 s_addr; 
};
struct sockaddr_in6 { 
  unsigned short int sin6_family;/* AF_INET6 */
  __be16 sin6_port; /* Transport layer port # */
  __be32 sin6_flowinfo;/* IPv6 address */
  struct in6_addr sin6_addr; /* IPv6 address */
  __u32 sin6_scope_id; /*scope id (new in RFC2553) */
};
struct in6_addr { 
  union {
    __u8  u6_addr8[16];
    __be16 u6_addr16[8];
    __be32 u6_addr32[4];
  } in6_u;
  #define s6_addr in6_u.u6_addr8
  #define s6_addr16 in6_u.u6_addr16
  #define s6_addr32 in6_u.u6_addr32
#define UNIX_PATH_MAX 108
struct sockaddr_un {
  __kernel_sa_family_t sun_family; /* AF_UNIX */
  char sun_path[UNIX_PATH_MAX]; /* pathname */ 
};


Pv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包 括16位端口号和32位IP地址,IPv6地址用sockaddr_in6结构体表示,包括16位端口号、128 位IP地址和一些控制字段。UNIX Domain Socket的地址格式定义在sys/un.h中,用sock- addr_un结构体表示。各种socket地址结构体的开头都是相同的,前16位表示整个结构 体的长度(并不是所有UNIX的实现都有长度字段,如Linux就没有),后16位表示地址类 型。IPv4、IPv6和Unix Domain Socket的地址类型分别定义为常数AF_INET、AF_INET6、AF_UNIX。 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构 体,就可以根据地址类型字段确定结构体中的内容。因此,socket API可以接受各种类型的 sockaddr结构体指针做参数,例如bind、accept、connect等函数,这些函数的参数应该设 计成void *类型以便接受各种类型的指针,但是sock API的实现早于ANSI C标准化,那时还 没有void *类型,因此这些函数的参数都用struct sockaddr *类型表示,在传递参数之前 要强制类型转换一下,例如:


struct sockaddr_in servaddr;
/* initialize servaddr */
bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));


4. 总结


本文介绍了网络字节序概念以及字节序转换C函数、IP地址转换函数、sockaddr数据结构等。

目录
相关文章
|
10月前
|
存储 消息中间件 人工智能
【03】AI辅助编程完整的安卓二次商业实战-本地构建运行并且调试-二次开发改注册登陆按钮颜色以及整体资源结构熟悉-优雅草伊凡
【03】AI辅助编程完整的安卓二次商业实战-本地构建运行并且调试-二次开发改注册登陆按钮颜色以及整体资源结构熟悉-优雅草伊凡
296 3
|
10月前
|
存储 消息中间件 人工智能
【05】AI辅助编程完整的安卓二次商业实战-消息页面媒体对象(Media Object)布局实战调整-按钮样式调整实践-优雅草伊凡
【05】AI辅助编程完整的安卓二次商业实战-消息页面媒体对象(Media Object)布局实战调整-按钮样式调整实践-优雅草伊凡
283 11
【05】AI辅助编程完整的安卓二次商业实战-消息页面媒体对象(Media Object)布局实战调整-按钮样式调整实践-优雅草伊凡
|
10月前
|
存储 消息中间件 人工智能
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
640 10
【08】AI辅助编程完整的安卓二次商业实战-修改消息聊天框背景色-触发聊天让程序异常终止bug牵涉更多聊天消息发送优化处理-优雅草卓伊凡
|
10月前
|
存储 消息中间件 人工智能
【04】AI辅助编程完整的安卓二次商业实战-寻找修改替换新UI首页图标-菜单图标-消息列表图标-优雅草伊凡
【04】AI辅助编程完整的安卓二次商业实战-寻找修改替换新UI首页图标-菜单图标-消息列表图标-优雅草伊凡
586 4
|
10月前
|
XML 存储 Java
【06】AI辅助编程完整的安卓二次商业实战-背景布局变更增加背景-二开发现页面跳转逻辑-替换剩余图标-优雅草卓伊凡
【06】AI辅助编程完整的安卓二次商业实战-背景布局变更增加背景-二开发现页面跳转逻辑-替换剩余图标-优雅草卓伊凡
235 3
【06】AI辅助编程完整的安卓二次商业实战-背景布局变更增加背景-二开发现页面跳转逻辑-替换剩余图标-优雅草卓伊凡
|
安全 算法 Ubuntu
Linux(openssl)环境:编程控制让证书自签的技巧。
总结:在Linux环境中,OpenSSL是一个非常实用的工具,可以帮助我们轻松地生成自签名证书。通过上述三个简单步骤,即可为内部网络、测试环境或开发环境创建自签名证书。但在公共访问场景下,建议购买经过权威认证机构签发的证书,以避免安全警告。
712 13
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
1020 1
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
325 26
|
JavaScript Ubuntu Linux
如何在阿里云的linux上搭建Node.js编程环境?
本指南介绍如何在阿里云Linux服务器(Ubuntu/CentOS)上搭建Node.js环境,包含两种安装方式:包管理器快速安装和NVM多版本管理。同时覆盖全局npm工具配置、应用部署示例(如Express服务)、PM2持久化运行、阿里云安全组设置及外部访问验证等步骤,助你完成开发与生产环境的搭建。
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
345 17

热门文章

最新文章