Linux 网络编程基础(1)--网络相关的数据结构及转化函数
在Linux下进行网络编程,使用的语言一般为C。就个人感受而言,在Linux下进行网络程序的编写,重要的不是代码能力要多强,而是对Linux的网络编程思想的理解和对Linux网络数据结构的掌握。如果想要掌握一般的Linux网络代码编写,首要的任务是理解TCP和UDP协议,要在写代码的时候清晰的知道某段代码的作用是什么,对应的是网络协议哪一个步骤。这样才能够深刻的理解代码,慢慢的写出真正属于自己的代码,而不是人云亦云,亦步亦趋。
网络相关的数据结构
当然只是把IPv4和IPv6的常用数据结构总结一下,网络相关的数据结构还是很多的,Unix域的数据结构另外再说。
首先介绍struct sockaddr、struct sockaddr_in、struct in_addr。
typedef unsigned short sa_family_t;
struct sockaddr {
sa_family_t sa_family; /* address family, AF_XXX */
char sa_data[14]; /* 14 bytes of protocol address */
};
结构体struct sockaddr定义在/usr/include/linux/socket.h中。
struct sockaddr_in {
sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
/* 字符数组sin_zero[8]的存在是为了保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等 */
};
结构体struct sockaddr_in在/usr/include/netinet/in.h中定义。
struct in_addr{
unsigned long s_addr;
};
in_addr其实就是32bit的IP地址。
struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。
主机字节序和网络字节序
在介绍上述三个结构体的成员变量之前,先介绍一下字节序。由于网络的特点是将网络上不同的设备和主机进行连接和通信,这就要求开发的网络程序要兼容不同的设备,数据对于设备要有唯一的含义,字节序就是上述问题的典型代表。字节序分为两类:大端字节序和小端字节序。
小端字节序(Little Endian):将数据的最低字节放在内存的起始位置。特点是内存的低位放置数据的低位。
大端字节序(Big Endian):将数据的高字节放在内存的起始位置。特点是内存的低位放置数据的高位。与思维习惯不一致,但是与数据的表达是一致的。比如一个四字节的数据0x12345678,起始的内存位置为0x1000;按照大端和小端的存放规范分别是:
内存地址 0x1000 0x1001 0x1002 0x1003
小端模式 0x78 0x56 0x34 0x12
大端模式 0x12 0x34 0x56 0x78
网络的字节序为大端字节序,所以在进行网络传输前,要将本地的数据转化为网络字节序,到达主机后,再转化为主机字节序。为了完成字节序的转化,Linux提供了一些转化的API,稍候再讨论相关的细节。那要是不知道一台机器的字节序,怎么通过代码来检查此主机的字节序呢?
结构体 struct sockaddr_in
重点分析一下sockaddr_in结构体,它是Intnet域网络编程的重要的数据结构之一。一一分析一下词数据结构的成员。
1、sin_family可以指定当前的网络程序遵循IPv4还是IPv6,经典的赋值方式是:
struct sockaddr_in skt_addr;
skt_addr.sin_family = AF_INET; /*使用IPv4版本*/
skt_addr.sin_family = AF_INET6; /*使用IPv6版本*/
2、sin_port是一个网络字节序的16整型。一定当前的网络程序的端口(此处的端口可以十分简化的理解为:不同网络程序的标记,不同的网络程序使用不同的端口,这样不同网络服务之间就可以进行区分了,当然这种理解是片面)。端口大致可以分为三类:
1.周知端口(Well Known Ports)
周知端口是众所周知的端口号,范围从0到1023,其中80端口分配给www服务,21端口分配给FTP服务等。我们在IE的地址栏里输入一个网址的时候是不必 指定端口号的,因为在默认情况下www服务的端口 号是“80”。所以这个范围的端口就不能自己随便用了。
2.动态端口(Dynamic Ports)
动态端口的范围是从49152到65535。之所以称为动态端口,是因为它 一般不固定分配某种服务,而是动态分配。动态分配是指当一个系统进程或应用 程序进 程需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配 一个供它使用。当这个进程关闭时,同时也就释放了所占用的端口号。
3.注册端口
端口1024到49151,分配给用户进程或应用程序。这些进程主要是用户选择安装的一些应用程序,而不是已经分配好了公认端口的常用程序。这些端口在没有 被服务器资源占用的时候,可以用用户端动态选用为源端口。
所以一般情况下自己写程序,使用的都是注册端口。好了,假如我们已经知道我们在IPv4协议下进行通信,使用端口8888,那代码怎么写?
struct sockaddr_in skt_addr;
skt_addr.sin_family = AF_INET;
skt_addr.sin_port = 8888; /*这么写就错了,为啥??!!*/
原因就是我们要保证所有的数据都是网络字节序的。这个时候字节序的转化函数就很有必要了。为了程序设计的方便,
Linux提供了平台无关的四个字节转换函数:
#include<arpa/inet.h>
uint32_t htonl(uint32_t hostlong); /*长整型 主机字节序到网络字节序*/
uint16_t htons(uint16_t hostlong); /*短整型 主机字节序到网络字节序*/
uint32_t ntohl(uint32_t hostlong); /*长整型 网络字节序到主机字节序*/
uint16_t ntohs(uint16_t hostlong); /*短整型 网络字节序到主机字节序*/
所以上面的代码应该这么写:
struct sockaddr_in skt_addr;
skt_addr.sin_family = AF_INET;
skt_addr.sin_port = htons(8888); /*这么写就可以了*/
3、 struct in_addr sin_addr 这个成员其实我们很熟悉了,非计算机的人也听说过IP地址。这个就是32bit的IP地址。那编程时对这个成员赋与一个点分十 进制的IP地址,就可以使我们的数据在网络上传输了?显然不可以的。
计算机的存储和人的认知之间是有差距的,“127.0.0.1”这样的IP地址当然是直观的,但是在网络中需要对此数据进行修饰。修饰的函数如下:
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp); /*将点分十进制的IP地址转化为in_addr*/
in_addr_t inet_addr(const char *cp); /*将点分十进制的IP地址转化为in_addr, 不过返回值不一样了*/
有了网络上的IP,怎么转化为可读的IP地址呢?
char * inet_ntoa(struct in_addr in); /*将in_addr转化为点分十进制的IP地址*/
其实现在使用的转化函数为inet_ntop()和inet_pton(); 因为这两个函数是可重入的,而且支持多种地址类型。
int inet_pton(int af, const char *src, void *dst);
af: 指定地址类型,AF_INET或者INET_INET6
src: 点分十进制的IP地址
dst: 转化后的网络IP地址,一般接收到后,使用强制类型转换得到in_addr
char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
af: 指定地址类型,AF_INET或者INET_INET6
src: 待转化的网络地址
dst: 接收点分十进制的IP地址
cnt:dst的大小,若此值设置过小,会导致ENOSPC错误。
到目前为止,我们可以完整的定义并且初始化一个sockaddr_in 结构体了。
struct sockaddr_in skt_addr;
skt_addr.sin_family = AF_INET;
skt_addr.sin_port = htons(8888); /*这么写就可以了*/
skt_addr.sin_addr = inet_pton("127.0.0.1");
基本的数据结构介绍到此。
下一篇:获取主机信息、协议处理函数
Linux 网络编程基础(1)--网络相关的数据结构及转化函数的更多相关文章
- 网络编程基础:网络基础之网络协议、socket模块
操作系统(简称OS)基础: 应用软件不能直接操作硬件,能直接操作硬件的只有操作系统:所以,应用软件可以通过操作系统来间接操作硬件 网络基础之网络协议: 网络通讯原理: 连接两台计算机之间的Intern ...
- java网络编程基础——TCP网络编程一
基于TCP协议的网络编程 TCP/IP协议是一种可靠的网络协议,它的通信的两端各自建立一个Socket,从而在通信的两端之间形成网络虚拟链路. Java使用Socket对象来代表两端的通信端口,并通过 ...
- java网络编程基础——基本网络支持
基本网络支持 java.net包主要为网络编程提供支持. 1.InetAddress InetAddress类代表IP地址,还有两个子类:Inet4Address.Inet6Address. pack ...
- java网络编程基础——TCP网络编程二
1.半关闭的Socket 前面的服务器和客户端通信时总是以行为最小数据单位,但是在某些协议里,通信的数据单位可能是多行的,当出现多行数据时就 出现一个问题:Socket输出流如何表示输出数据已经结束. ...
- java网络编程基础——TCP网络编程三
AIO实现非阻塞通信 java7 NIO2 提供了异步Channel支持,这种异步Channel可以提供更高效的IO,这种基于异步Channel的IO被称为异步IO(Asynchronous IO) ...
- Java网络编程和NIO详解开篇:Java网络编程基础
Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...
- Linux 高性能服务器编程——Linux网络编程基础API
问题聚焦: 这节介绍的不仅是网络编程的几个API 更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系. 这节主要介绍三个方面的内容:套接字(so ...
- 第5章 Linux网络编程基础
第5章 Linux网络编程基础 5.1 socket地址与API 一.理解字节序 主机字节序一般为小端字节序.网络字节序一般为大端字节序.当格式化的数据在两台使用了不同字节序的主机之间直接传递时,接收 ...
- 服务器编程入门(4)Linux网络编程基础API
问题聚焦: 这节介绍的不仅是网络编程的几个API 更重要的是,探讨了Linux网络编程基础API与内核中TCP/IP协议族之间的关系. 这节主要介绍三个方面的内容:套接字( ...
随机推荐
- 漫谈servlet技术
1.要谈到Servlet技术,不得不先谈谈动态网页的概念. 编写过网页的人都知道,浏览器能够根据HTML静态标记语言来显示各式各样的网页.但是如果我们需要在网页上完成一些业务逻辑:比如登陆验证.或者说 ...
- 安全的PHP代码编写准则
原文链接 绝不要信任外部数据或输入 关于 Web 应用程序安全性,必须认识到的第一件事是不应该信任外部数据.外部数据(outside data) 包括不是由程序员在 PHP 代码中直接输入的任何数据. ...
- Linux学习之系统时间同步
一.系统时间的设置 在Linux中设置系统时间,可以用date命令: //查看时间 [root@localhost ~]# date 2008年 12月 12日 星期五 :: CST //修改时间 [ ...
- mysql 索引创建规则
1.表的主键.外键必须有索引:2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特别是大表的字段,应该建立索引: 5 ...
- JavaWeb核心编程之(三.5)HTTP请求和接受表单数据
HTTP简介WEB浏览器与WEB拂去其之间的一问一答的交互过程, 必须遵循一定的规则,这个规则就是HTTP协议HTTP是hypertext transfer protocol(超文本传输协议)的简写, ...
- IE的事件与w3c事件的区别
14. offsetWidth, scrollLeft, scrollHeight? scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离 scrollHeig ...
- SQL Server 输出受影响的行
前期准备: create table Nums(X int); create table T(X int); go 目的:把对表Nums的insert | delete | update 反映到T表中 ...
- GDAL python教程(1)——用OGR读写矢量数据
本教程的讲义和源码都是取自Utah State University的openGIS课程 相关资料,包括讲义.源码.数据样例,请从此处下载http://www.gis.usu.edu/~chrisg/ ...
- A类型物料必须为装配拉式,供应子库为B仓
应用 Oracle Bill Of Materiel 层 Level Function 函数名 Funcgtion Name MT_BOMFDBOM 表单名 Form Name BOMFDBOM ...
- uva 10003 Cutting Sticks (区间dp)
本文出自 http://blog.csdn.net/shuangde800 题目链接: 打开 题目大意 一根长为l的木棍,上面有n个"切点",每个点的位置为c[i] 要按照一 ...