UNIX网络编程——socket概述和字节序、地址转换函数
一、什么是socket
socket可以看成是用户进程与内核网络协议栈的编程接口。
socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信。
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及以后要讲的UNIX Domain Socket。然而,各种网络协议的地址格式并不相同,如下图所示:
IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位端口号和32位IP地址,如下所示:
struct sockaddr_in {
uint8_t sin_len; /*length of structure (16)*/
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
char sin_zero[8]; /* pad bytes, set to zero is ok */
};
struct in_addr{
in_addr_t s_addr; /*32-bit IPV4 address*/
};
sa_family_t是一个无符号短整型(unsigned short)。in_addr_t数据类型必须是一个至少32位的无符号整数类型,in_port_t必须是一个至少16位的无符号的整数类型。
IPv6地址用sockaddr_in6结构体表示,包括16位端口号、128位IP地址和一些控制字段。UNIX Domain Socket的地址格式定义在sys/un.h中,用sockaddr_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 {
uint8_t sa_len;
sa_family_t sin_family;
char sa_data[14];
};
sin_family:指定该地址家族
sa_data:由sin_family决定它的形式。
在传递参数之前要强制类型转换一下,例如:
struct sockaddr_in servaddr;
/* initialize servaddr *
/bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
二、网络字节序
字节序
大端字节序(Big Endian)
最高有效位(MSB:Most Significant Bit)存储于最低内存地址处,最低有效位(LSB:Lowest Significant Bit)存储于最高内存地址处。
小端字节序(Little Endian)
最高有效位(MSB:Most Significant Bit)存储于最高内存地址处,最低有效位(LSB:Lowest Significant Bit)存储于最低内存地址处。
主机字节序
不同的主机有不同的字节序,如x86为小端字节序,Motorola 6800为大端字节序,ARM字节序是可配置的。
网络字节序
网络字节序规定为大端字节序
为使网络程序具有可移植性,使同样的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位短整数。例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
下面写个小程序测试下主机的大小端:
#include<stdio.h>
#include<arpa/inet.h> int main(void)
{
unsigned int x = 0x12345678;
unsigned char *p = (unsigned char *)&x;
printf("%x %x %x %x\n", p[0], p[1], p[2], p[3]); unsigned int y = htonl(x);
p = (unsigned char *)&y;
printf("%x %x %x %x\n", p[0], p[1], p[2], p[3]); return 0;
}
运行结果:
huangcheng@ubuntu:~$ ./a.out
78 56 34 12
12 34 56 78
即本主机是小端字节序,而经过htonl 转换后为网络字节序,即大端。
三、地址转换函数
前面提到的 sockaddr_in 结构体中的成员struct in_addr sin_addr表示32位的IP地址。但是我们通常用点分十进制的字符串表示IP地址,以下函数可以在字符串表示和in_addr表示之间转换。
字符串转in_addr的函数:
#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr);
in_addr_t inet_addr(const char *strptr);
int inet_pton(int family, const char *strptr, void *addrptr);
注意:转换而成的32位数是网络字节序的。
in_addr转字符串的函数:
char *inet_ntoa(struct in_addr inaddr);
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
注意:传入的32位数也是网络字节序的。
其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口是void *addrptr。
下面写个小程序演示一下:
#include<stdio.h>
#include<arpa/inet.h> int main(void)
{ unsigned int addr = inet_addr("192.168.0.100"); //转换后是网络字节序(大端)
printf("add=%u\n", ntohl(addr)); struct in_addr ipaddr;
ipaddr.s_addr = addr;
printf("%s\n", inet_ntoa(ipaddr)); return 0;
}
运行结果:
huangcheng@ubuntu:~$ ./a.out
add=3232235620
192.168.0.100
注意,在打印addr的时候先转换成主机字节序,否则输出可能是负数。
四、套接字类型
流式套接字(SOCK_STREAM)
提供面向连接的、可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收。
数据报式套接字(SOCK_DGRAM)
提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。
原始套接字(SOCK_RAW)
UNIX网络编程——socket概述和字节序、地址转换函数的更多相关文章
- 《Unix 网络编程》11:名字和地址转换
名字和地址转换 系列文章导航:<Unix 网络编程>笔记 域名系统 简介 域名系统主要用于主机名字和 IP 地址之间的映射.主机名可以是: 简单名字,如:centos01 全限定域名(FQ ...
- UNIX网络编程读书笔记:名字与地址转换
概述 在名字和数值地址间进行转换的函数: gethostbyname和gethostbyaddr:在主机名字与IPv4地址之间进行转换.仅仅支持IPv4. getservbyname和getservb ...
- TCP和UDP的区别与联系以及网络字节序和主机字节序的转换函数实践
TCP和UDP的区别 TCP是一个面向连接的.可靠的.基于字节流的传输层协议. 而UDP是一个面向无连接的传输层协议. 具体来分析,和 UDP 相比,TCP 有三大核心特性: 面向连接:所谓的连接,指 ...
- socket概述和字节序、地址转换函数
一.什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口. socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信. socket API是一层抽象的网 ...
- 【网络编程一】主机字节序与网络字节序以及ip地址转换函数
在计算机设计之初,对内存中数据的处理也有不同的方式,(低位数据存储在低位地址处或者高位数据存储在低位地址处),然而,在通信的过程中(ISO/OSI模型和TCP/IP四层模型中),数据被一步步封装(然后 ...
- UNIX网络编程——Socket通信原理和实践
我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠so ...
- UNIX网络编程读书笔记:套接口地址结构
前言 大多数套接口函数都需要一个指向套接口地址结构的指针作为参数.每个协议族都定义它自己的套接口地址结构.这些结构的名字均以"sockaddr_"开头,并以对应每个协议族的唯一后缀 ...
- UNIX网络编程——I/O复用:select和poll函数
我们看到TCP客户同时处理两个输入:标准输入和TCP套接字.我们遇到的问题是就在客户阻塞于(标准输入上)fgets调用,服务器进程会被杀死.服务器TCP虽然正确的给客户TCP发送了一个FIN,但是既然 ...
- UNIX网络编程——Socket粘包问题
一.两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通讯连接,连接建立后不断开, 然后再进行报文发送和接收. 2.短连接 Client方与Server每进行一次报文收发交易 ...
随机推荐
- 数据权限管理中心 - 基于mybatis拦截器实现
数据权限管理中心 由于公司大部分项目都是使用mybatis,也是使用mybatis的拦截器进行分页处理,所以技术上也直接选择从拦截器入手 需求场景 第一种场景:行级数据处理 原sql: select ...
- c语言第三次作业。
---恢复内容开始--- (一)改错题 计算f(x)的值:输入实数x,计算并输出下列分段函数f(x)的值,输出时保留1位小数. 源代码 : 第一次编译: 错误原因:if 后面有分号 改正方法:去掉分号 ...
- Tensorflow从入门到精通之——Tensorflow基本操作
前边的章节介绍了什么是Tensorflow,本节将带大家真正走进Tensorflow的世界,学习Tensorflow一些基本的操作及使用方法.同时也欢迎大家关注我们的网站和系列教程:http://ww ...
- TensorFlow-Bitcoin-Robot:Tensorflow 比特币交易机器人
简介 一个比特币交易机器人基于 Tensorflow LSTM 模型,仅供娱乐. A Bitcoin trade robot based on Tensorflow LSTM model.Just f ...
- break 与 continue
1.break ①只有一层循环时,作用是跳出循环语句,执行后面的代码. ②break存在于循环嵌套的内层循环时,只能跳出内层循环,如果想要跳出外层循环,需要对外层循环添加标记. 2.continue ...
- oracle循环插入1万条数据
declare maxnumber constant number:=10000; i number :=1; begin for i in 1..maxnumber loop insert into ...
- Hibernate QBC 条件查询(Criteria Queries) and Demos
目录 创建一个Criteria 实例 限制结果集内容 结果集排序 关联 动态关联抓取 查询示例 投影Projections聚合aggregation和分组grouping 离线detached查询和子 ...
- SignalR Progress
Server public class ServerHub : Hub { public async Task<string> ALongTimeTask() { var p = new ...
- JS基础(二)
一.JS中的循环结构 循环结构的执行步骤 1.声明循环变量: 2.判断循环条件: 3.执行循环体操作: 4.更新循环变量 然后,循环执行2-4,直到条件不成立时,跳出循环. while循环()中的表达 ...
- Docker 备份、恢复、迁移数据卷
可以利用数据卷对其中的数据进行进行备份.恢复和迁移. 备份 首先使用 --volumes-from 标记来创建一个加载 dbdata 容器卷的容器,并从本地主机挂载当前到容器的 /backup 目录. ...