套接字编程简介
套接字地址结构
IPv4套接字地址结构
网际套接字地址结构
sockaddr_in命名,定义在<netinet/in.h>
头文件中
1 | struct in_addr { |
套接字地址结构仅在给定主机上使用
- 虽然结构总的某些字段(如IP地址和端口号)用在不同主机之前的通信中
- 但结构本身并不在主机之间传递
通用套接字地址结构
套接字地址结构总以引用形式传递
而使用这种参数的函数必须能处理任何协议族的套接字地址结构
ANSI C后可用void* 通用指针解决
但套接字函数在ANSI C之前定义
因此在<sys/socket.h>
头文件中定义一个通用的套接字地址结构
1 | struct sockaddr { |
套接字函数被定义为指向某个通用套接字地址结构的一个指针作为参数之一
如:int bind(int, struct sockaddr *, socklen_t);
因此任何调用都必须将特定协议的套接字地址结构的指针进行类型强制转换,变为通用套接字地址结构指针
如:struct sockaddr_in serv;
bind(sockfd, (struct sockaddr *) &serv, sizeof(serv));
IPv6套接字地址结构
在<netinet/in.h>
头文件中定义
1 | struct in6_addr { |
- 若系统支持套接字地址结构中的长度字段,则SIN6_LEN常值必须定义
- IPv6的地址族是AF_INET6,而IPv4的地址族是AF_INET
- 结构中字段的先后顺序做过编排,使得如果sockaddr_in6结构本身是64位对齐的,那么128位的sin6_addr字段也是64位对齐的
- sin6_flowinfo字段分成两个字段:
- 低序20位是流标
- 高序12位保留
- 对于具备范围的地址,sin6_scope_id字段标识其范围,最常见的是链路局部地址的接口索引
新的通用套接字地址结构
作为IPv6套接字API的一部分定义的新通用套接字地址结构
克服了现有的一些缺点
足以容纳系统所支持的任何套接字地址结构
在<netinet/in.h>
头文件中定义
1 | struct sockaddr_storage { |
相比之下sockaddr存在以下两点差别:
- sockaddr_storage能够满足最苛刻的对齐要求
- sockaddr_storage足够大,能够容纳系统支持的任何套接字地址结构
注:出ss_family和ss_len外,sockaddr_storage结构总的其他字段对用户来说是透明的
sockaddr_storage结构必须类型强制转换成或复制到适合ss_family字段所给出地址类型的套接字地址结构中,才能访问其他字段
值-结果参数
函数被调用时,结构大小是一个值,告诉内核该结构的大小,这样内核在写该结构时不至于越界
当函数返回时,结构大小又是一个结果,告诉进程内核在该结构中究竟存储了多少信息
这种类型的参数称为值-结果参数
字节排序函数
内存中存储两个字节的方法有两种:
- 将低序字节存储在起始地址:小端字节序
- 将高序字节存储在起始地址:大端字节序
网际协议使用大端字节序来传送这些多字节整数
主机字节序和网络字节序之间转换使用以下4个函数:
1 | #include <netinet/in.h> |
h代表host
n代表network
s代表short 现在为一个16为的值(端口号)
l代表long 现在为一个32位的值(IPv4地址)
使用时不关心主机字节序和网络字节序的真是值
与网际协议所用字节序相同的系统中,这四个函数通常为空宏
字节(byte)
大多因特网标准使用八位组表示一个8位的量
字节操纵函数
以b(表示字节)开头的函数起源于4.2BSD,支持套接字函数的系统仍然提供它们
mem(表示内存)开头的函数起源于ANSI C标准,支持ANSI C函数库的所有系统都提供它们
源自Berkeley的函数
1 | #include <strings.h> |
ANSI C函数:
1 | #include <string.h> |
当源字节串与目标字节串重叠时,bcopy能正确处理,但memcpy的结果不确定
此时必须改用ANSI C的memmove函数
memcpy中指针参数顺序与C中复制语句顺序相同:
dest = src;
ANSI C的memXXX函数都需要一个长度参数,且总是最后一个参数
inet_aton、inet_addr、inet_ntoa
函数
在点分十进制数串与32位网络字节序二进制之间转换IPv4地址
较新的函数inet_pton
和inet_ntop
对IPv4和IPv6地址都适用
1 | #include <arpa/inet.h> |
函数以结构为参数是罕见的,更常见的是以指向结构的指针作为参数
inet_pton、inet_ntop
函数
随IPv6出现的新函数,对IPv4和IPv6地址都适用
p:表达
n:数值
地址表达格式通常为ASCII字符串
数值格式则是存放到套接字地址结构中的二进制值
1 | #include <arpa/inet.h> |
这两个函数的family参数既可以是AF_INET也可以是AF_INET6
若以不被支持的地址族作为参数,则都返回一个错误,并将errno置为EAFNOSUPPORT
头文件<netinet/in.h>
中有定义:
1 | #define INET_ADDRSTRLEN 16 |
如果len太小,不够容纳结果(包括结尾的空字符),则返回一个空指针,置errno为ENOSPC
inet_ntop函数的strptr参数不可为空指针,必须自己分配内存并指定大小
示例:
旧:foo.sin_addr.s_addr = inet_addr(cp);
新:inet_pton(AF_INET, cp, &foo.sin_addr);
旧:ptr = inet_ntoa(foo.sin_addr);
新:char str[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
sock_ntop
和相关函数
inet_ntop需要调用者知道套接字地址结构的格式和地址族
这样代码就与协议相关了
为解决此问题,可自行编写名为sock_ntop的函数
- 以指向某个套接字地址结构的指针为参数
- 查看结构的内部
- 然后调用适当的函数返回该地址的表达格式
1
2
3
4#include "unp.h"
char *sock_ntop(const struct sockaddr *sockaddr, socklen_t addrlen);
返回:若成功则为非空指针,出错则为NULL
注:对结果进行静态存储导致该函数不可重入且非线程安全
- 标题: 套接字编程简介
- 作者: GuangYing
- 创建于 : 2024-12-24 22:19:16
- 更新于 : 2024-12-24 22:23:51
- 链接: http://quebo.cn/2024/12/24/套接字编程简介/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。