网络模型
物理层
物理层表示的是比特流传输,通常包括串口/COM口、并行/LPT口、USB、网线接口、电话线接口;其中串口包括智能仪表接口(RS-232)、RS-485(联网仪表接口);并行接口包括IEEE1284打印机扫描仪接口;USB接口包括USB2.0,USB3.0接口;网线接口:RJ45水晶头接口;电话线接口是RJ11;
数据链路层
数据链路层,用于实现网络控制,链路纠错;常用的协议包括:LLC(Logical Link Control)逻辑链路控制协议、MAC(Multiple Access Control)多路访问控制协议、PPP(Point to Point Protocol)点对点协议;
网络层
实现的功能是寻址和路由功能,常用的协议包括:IP(Internet Protocol)互联网协议、ARP(Address Resolution Protocol)地址解析协议、RARP(Reverse Address Resolution Protocol)反向地址解析协议、RIP(Routing Information Protocol)路由信息协议、ICMP(Internet Control Message Protocol)互联网报文控制协议、Enternet(以太网协议);
传输层
用于建立主机端到端的链接;TCP(Transmission Control Protocol)传输控制协议、UDP(User Datagram Protocol)用户数据报协议;
会话层
用于建立、维护和管理会话;没有相关的协议
表示层
格式转换,加密解密;也没有常见的协议;
应用层
用于提供应用进程通信,常用的协议包括:文件传输协议FTP(File Transfer Protocol)端口号为21;远程终端协议Telnet(Remote Terminal Protocol),端口号为23;简单邮件传输协议SMTP(Simple Mail Transfer Protocol),端口号为25;简单文件传输协议TFTP(Trivial File Transfer Protocol)端口号为69;超文本传输协议HTTP(Hypertext Transfer Protocol),端口号为80;安全套接层文本传输协议HTTPS(Hypertext Transfer Protocol over Secure Socket Layer),端口号为443;域名解析协议DNS(Domain Name Service),自举报协议BOOT(Bootstrap Protocol);
&8195;总结:
功能角度:
1、1,2层主要用于解决网络信道问题;
2、3,4层主要用于解决传输问题;
3、5,6,7层主要用于处理对应用进程的访问;
控制角度:
1、1,2,3,是通信子网层;
2、4,5,6,7层是主机控制层
数据封装:
No | 信息单位 | 层 |
---|---|---|
1 | 比特(bit) | 物理层 |
2 | 帧(frame) | 数据链路层 |
3 | 数据包(packet) | 网络层 |
4 | 段(segment)/数据报(datagram) | 传输层 |
5-7 | 消息(message) | 应用层 |
TCP/IP四层模型:
数据链路层,网络层,传输层,应用层;
scoket编程接口
转换操作
字节转换操作
网络序转主机序:ntohs(),network to host short,表示将unsigned short类型的从网络序转换到主机序;ntohl(),network to host long,把unsigned long类型从网络序转化到主机序;需要进行转换操作一般是端口;
remote_addr.sin_port = htons(atoi(argv[2]);
主机序转网络序:htons(),host to network short,把unsigned long类型从主机序转换到网络序;htonl(),host to network long,把unsigned long类型从主机序转换到网络序;
IP地址转换操作
IPv4专用:点分十进制数串转网络字节序长整型:inet_aton(const char *string,struct in_addr *addr);string:表示点分十进制IP地址字符串;addr:网络字节序长整型IP地址;返回值0表示成功,非0表示失败;in_addr_t inet_addr(const char *string);string:表示点分十进制IP地址字符串;返回值IPADDR_NONE表示失败,非INADDR_NONE:表示网络字节序长整型IP地址;网络序长整型转点分十进制数串:char (inet_ntoa(struct in_addr addr);addr:网络字节序长整型IP地址;返回值:非NULL:表示点分十进制IP地址字符串;NULL:表示失败;
IPv4/IPv6通用(推荐这个):点分十进制数串转网络字节序长整型:int inet_pton(int af, const char *src,void *dst);af:表示地址簇:AF_INET,AF_INET6;src:点分十进制IP地址串;dst:网络字节序长整型IP地址;返回值:<0:表示失败,返回值如果是0表示af或者src格式不对;网络字节序长整型转点分十进制数串:const char *inet_ntop(int af,const void *src,char *dst,socklen_t cnt);af:表示地址簇:AF_INET,AF_INET6;src:表示网络字节序长整型IP地址;dst:表示点分十进制IP地址字符串;cnt:表示缓冲区dst的大小;返回值NULL:表示失败;非NULL表示dst指针;
主机名转换操作
struct hostent {
h_name //表示主机名字
h_alicases //以空指针结尾的主机别名队列
h_addrtype //表示地址类型:AF_INET;AF_INET6;
h_length //表示地址长度,在AF_INET地址类型为4;
h_addr //第一个IP地址
h_addr_list //以空指针结尾的IP地址列表
}
主机名转地址:struct hostent *gethostbyname(const char *hostname);hostname:表示主机名;返回值:NULL,表示出错;非NULL:表示hostent结构指针;
mygethostbyname.c
#include
#include
#include
#include
int main(int argc, char **argv)
{
char *ptr, **pptr;
struct hostent *addr;
char str[32];
ptr = argv[1];
if(2!=argc){
printf("Usage:%s \n",argv[0]);
return 0;
}
if((addr = gethostbyname(ptr)) == NULL)
{
printf(" gethostbyname error for host:%s\n", ptr);
return 0;
}
printf("hostname len:%d\n",addr->h_length);
printf("hostname type:%d\n",addr->h_addrtype);
printf("official hostname:%s\n",addr->h_name);
for(pptr = addr->h_aliases; *pptr != NULL; pptr++)
printf(" alias:%s\n",*pptr);
switch(addr->h_addrtype)
{
case AF_INET:
case AF_INET6:
pptr=addr->h_addr_list;
for(; *pptr!=NULL; pptr++)
printf(" address:%s\n",
inet_ntop(addr->h_addrtype, *pptr, str, sizeof(str)));
printf(" first address: %s\n",
inet_ntop(addr->h_addrtype, addr->h_addr, str, sizeof(str)));
break;
default:
printf("unknown address type\n");
break;
}
return 0;
}
地址转主机名:sruct hostent *gethostbyaddr(const char *addr,int len,int type);addr:网络字节顺序地址;len:地址的长度,在AF_INET类型地址中为4;type:地址类型:AF_INET,AF_INET6;返回值:NULL表示出错,非NULL表示hostent结构指针;
gethostbyaddr.c
scoket操作
常见操作
创建操作:int socket(int domain,int type,int protocol);domain:表示协议域:AF_INET,表示IPv4,AF_INET6,表示IPv6;AF_LOCAL:表示unix域;type:SCOKET_STREAM:流式套接字,提供面向连接、可靠地数据传输服务, 数据按照字节流、按顺序收发,保证在传输过程中无丢失,无冗余;TCP协议支持该套接字;SOCKET_DGRAM:数据报套接字,提供面向无连接的服务,数据收发无序,不能能够保证数据的准确到达,UDP协议支持该套接字;SCOK_RAMW:表示原始套接字,只允许对于低于传输层的协议或物理网络直接访问,经常用于检测新的协议;
int listenfd = socket(AF_INET,SOCKSTREAM,0)
类型 | 作用 |
---|---|
流式套接字(SOCKET_STREAM) | 提供了一个面向连接的,可靠地数据传输服务,数据无差错,无重复的发送且按照发送顺序接收,内置流量控制,避免数据流淹没慢的接收方。数据被看做字节流,无长度限制; |
数据包套接字(SOCKET_DGRAM) | 提供无连接服务,数据报以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接受; |
原始套接字(SOCKET_RAM) | 可以对较低层次协议,如IP,ICMP直接访问; |
Protocol:协议:0:表示自动根据type匹配协议;IPPROTO_TCP:表示TCP协议;IPPROTO_UDP:表示使用UDP协议;
返回值:-1:表示失败,>0:socket描述符;
关闭操作:int close(int fd);或者使用函数:int shurdown(int socketfd,int howto);socket:socket套接字;howto:SHUT_RD:值为0表示关闭连接的流;SHUT_WR:值为1表示关闭连接的写;SHUT_RDWR:值为2表示连接的读和写都关闭;
这两个函数的区别:close把描述符的引用计数减1,仅在计数变为0时才关闭套接字;shutdown不管引用计数器就激发TCP的正常连接终止序列;2、close终止读和写两个方向的数据传送;shutdown可以指定哪个方向被关闭,或者是都关闭;
close(connfd);
close(listenfd);
属性:设置socket套接字:int setsockopt(int socketfd, int level,int optname,const void *optval,socklen_t optlen);socketfd:套接字描述符;level:选项层次:SOL_SOCKET:通用套接字选项;level:选项层次:SOL_SOCKET:表示通用套接字选项;IPPROTO_TCP:表示TCP选项;IPPROTO_IP:表示IP选项;IPPROTO_IPv6:表示IPv6选项;
optname:
SOL_SOCKET级别:
SO_REUSEADDR 让端口释放后立即就可以被再次使用,一个端口释放后,会等待两分钟之后才能够被再次使用;
SO_RCVBUF 接收确定缓冲区大小
SO_SNDBUF 发送缓冲区大小
SO_SNDTIMEO 表示发送实现
SO_RECVTIMEO 表示接受实现
SO_BROADCAST 广播
SO_DONTLINGER 关闭端口后不进入TIME_WAIT状态
SO_LINGER 关闭端口后,进入TIME_WAIT状态的属性;
struct linger {
u_short l_onoff;
u_short l_linger;
}
IPPROTO_IP级别:IP_ADD_MEMBERSHIP:加入指定的组播组
struct ip_mreq{
struct in_addr imr_multiaddr;//组播组的IP地址;
struct in_addr imr_interface;//本地某一网络设备接口的IP地址;
}
IP_DROP_MEMBERSHIP:表示离开指定的组播组;IP_MULTCAST_IF:指定发送组播数据的IP地址;IP_MULTICAST_LOOP:发送组播数据的主机是否作为接收组播数据的组播成员;optval:选项值指针;optlen:optval缓冲区长度;返回值0表示成功,-1表示失败;
int flag =1;
setsockopt(listenfd,SQL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag));
属性的获取:int getoptsockopt(int sockfd,int level,int optname,void *optval,socklen_t *optlen);sockfd:表示套接字描述符;level:表示选项层次:SOL_SOCKET:通用套接字选项;IPPROTO_TCP:TCP选项;IPPROTO_IP:表示IP选项;IPPROTO_IPv6:表示IPv6选项;
optname:选项:SOL_SOCKET级别:SO_REUSEADDR:让端口释放后可以立即被再次使用,一二端口释放后等待两分钟之后才能够被再次使用;SO_RECVBUF:表示结束确定缓冲区大小;SO_SNDBUF:表示发送缓冲区大小;SO_SNDTIMEO:表示发送时限;SO_RECVTIMEO:接收时限;SO_BROADCAST:表示广播;SO_DONTLINGER:表示关闭端口后不进入TIME_WAIT状态;SO_LINGER:端口的状态进入TIME_WAIT的属性;
struct linger {
u_short l_onoff;
u_short l_linger;
};
IPPROTO_IP级别:IP_ADD_MEMBERSHIP,表示加入指定的组播组;IP_DROP_MEMBERSHIP:表示离开指定的组播组;IP_MULTICAST_IF:指定发送组播数据的IP地址;IP_MULTICAST_LOOP:发送组播数据的主机是否作为接收组播数据的组播成员;
optval:选项指针;optlen:optval缓冲区长度;返回值0表示成功,-1表示失败;
绑定操作:int bind(int socket,const struct sockaddr *address,socklen_t address_len);socket:表示套接字描述符;address:地址和端口号;address_len:address缓冲区的长度;返回值:0表示成功,SOCKET_ERROR:表示失败;
bind(listenfd,(struct sockaddr*)&local_addr,sizeof(local_addr)));
返回值为-1表示出错;
监听操作:int listen(int sockfd,int backlog);sockfd:监听的socket描述符;backlog:排队的最大连接数目;返回值:0表示成功,-1表示失败;
listen(listenfd,10)
-1表示失败,10表示队列中允许的最大链接数目;
连接操作:int connect(int sockfd,const struct sockaddr* addr,socket_t addrlen);sockfd:客户端的socket描述字;addr:服务器的socket地址;addrlen:服务器的socket地址的长度;返回值0表示成功,-1表示失败;
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
remote_addr.ain_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(argv[1]);
remote_addr.sin_port = htons(atoi(argv[2]);
connect(connfd,struct sockaddr*)&remote_addr,sizeof(remote_addr));
接受操作:int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);sockfd:表示服务器的socket描述符,用于监听的socket描述符;addr:client的socket地址;addrlen:client的socket地址长度;返回值:-1表示失败;非-1值表示链接描述符;
accept(listenfd,(struct sockaddr*)&remote_addr,&remote_addr_len);
返回值-1表示失败;
发送操作:ssize_t wite(int fd, const void* buf,size_t len);fd:表示文件描述符;buf:表示用于写入数据的位置;len:写入数据的长度;返回值>0表示所写的字节的长度;<0表示出错;
ssize_t send(int socked,const void *buf, size_t len,int flags);sockfd:表示sockfd文件描述符;buf:用于写入数据;len:写入数据的实际长度;flags:通常为0;>0:写入数据的实际长度;<0:表示出错;ssize_t sendto(int sockfd,const void *buf,size_t len,ing flags,const struct sockaddr *dest_addr,socklen_t addrlen);sockfd:表示sockfd文件描述符;buf:表示写入数据;len:表示写入数据的实际长度;flags:通常为0;dest_addr:目标socket地址;addrlen:目标socket地址的长度;返回值:>0表示写入数据的实际字节数;<0:表示出错;
接收操作:ssize_t read(int fd,void *buf,size_t len);fd:表示文件描述符;buf:表示读取数据;len:表示读取数据的长度;返回值:0:表示读取到文件的结束;>0:书籍所读取的字节数;<0:表示出错;ssize_t recv(int sockfd,void *buf,size_t len,int flags);fd:表示文件描述符;buf:表示读取的数据;len:表示读取数据的长度;flags:通常设置为0;返回值:0表示读取到文件的结束;>0:表示所读取的实际的字节数;<0:表示出错;ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen);fd:文件描述符;buf:表示用于读取数据;len:读取数据的长度;flags:通常为0;dest_addr:目标socket地址;addrlen:目标socket地址长度;返回值:0:表示读取为文件的结束;>0:表示实际所读的字节数;<0:表示出错;
#######几个相关的结构体
struct sockaddr {
unisigned short sa_family;
char sa_data[14];
};
IPv4:
struct sockaddr_in {
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr {
in_addr_t s_addr;
};
typedef unsigned int in_addr_t;
IPv6:
struct sockaddr_in6 {
uint8_t sin6_len;//IPv6为固定的24字节长度;
sa_family_t sin6_family; //地址簇类型,为AF_INET6;
in_port_t sin6_port; //16位端口号,网络字节序;
unit32_t sin6_flowinfo;//32位流标签;/128位IP地址;
struct in6_addr sin6_addr;
};
知识总结
特殊地址设置:通配地址:IPv4:in_addr.sin_addr.s_addr = htonl(INADDR_ANY);IPv6:in6_addr.sin6_addr = in6adddr_any;回环地址:
IPv4:in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);IPv6:in6_addr.sin_addr = in6adr_loopback;广播地址:IPv4:in_addr.s.addr = htonl(INADDR_BROADCAST);IPv6:没有广播;
原则
C/S:client/server:必须制定连接/发送的IP(广播地址,回环地址或者某个具体地址);必须制定连接发送的port;
S/C:server/client:IP指定为通配地址,回环地址或者某个具体地址;必须制定绑定监听/接收的port;
基本流程
TCP:Server:socket-->bind-->listen-->accept-->recv/read-->send/write;Client:socket-->connect-->send/write-->recv/read;UDP:单播,组播/广播;
tcp_client.c
#include
#include
#include
#include
void show_info(int connfd){
struct sockaddr_in local_addr;
bzero(&local_addr,sizeof(local_addr));
socklen_t local_addr_len = sizeof(local_addr);
getsockname(connfd,(struct sockaddr*)&local_addr,&local_addr_len);
printf("client local %s:%d\n",inet_ntoa(local_addr.sin_addr),ntohs(local_addr.sin_port));
struct sockaddr_in peer_addr;
bzero(&peer_addr,sizeof(peer_addr));
socklen_t peer_addr_len = sizeof(peer_addr);
getpeername(connfd,(struct sockaddr*)&peer_addr,&peer_addr_len);
printf("clinet peer %s:%d\n",inet_ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port));
}
int main(int argc,char* argv[]){
if(4 != argc){
printf("usage:%s <#port> \n",argv[0]);
return 1;
}
int connfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == connfd){
perror("socket err");
return 1;
}
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(argv[1]);
remote_addr.sin_port = htons(atoi(argv[2]));
if(-1 == connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))){
perror("connect err");
return 1;
}
show_info(connfd);
write(connfd,argv[3],strlen(argv[3])+1);
printf("client send:%s\n",argv[3]);
char buf[BUFSIZ];
bzero(buf,BUFSIZ);
if(-1 == read(connfd,buf,BUFSIZ)){
perror("read err");
return 1;
}
printf("client recv:%s\n",buf);
close(connfd);
}
tcp_client02.c
#include
#include
#include
#include
void show_info(int connfd){
struct sockaddr_in local_addr;
bzero(&local_addr,sizeof(local_addr));
socklen_t local_addr_len = sizeof(local_addr);
getsockname(connfd,(struct sockaddr*)&local_addr,&local_addr_len);
printf("client local %s:%d\n",inet_ntoa(local_addr.sin_addr),ntohs(local_addr.sin_port));
struct sockaddr_in peer_addr;
bzero(&peer_addr,sizeof(peer_addr));
socklen_t peer_addr_len = sizeof(peer_addr);
getpeername(connfd,(struct sockaddr*)&peer_addr,&peer_addr_len);
printf("clinet peer %s:%d\n",inet_ntoa(peer_addr.sin_addr),ntohs(peer_addr.sin_port));
}
int main(int argc,char* argv[]){
if(3 != argc){
printf("usage:%s <#port> \n",argv[0]);
return 1;
}
int connfd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == connfd){
perror("socket err");
return 1;
}
struct sockaddr_in remote_addr;
bzero(&remote_addr,sizeof(remote_addr));
remote_addr.sin_family = AF_INET;
remote_addr.sin_addr.s_addr = inet_addr(argv[1]);
remote_addr.sin_port = htons(atoi(argv[2]));
if(-1 == connect(connfd,(struct sockaddr*)&remote_addr,sizeof(remote_addr))){
perror("connect err");
return 1;
}
show_info(connfd);
char buf[BUFSIZ];
bzero(buf,BUFSIZ);
while(fgets(buf,BUFSIZ,stdin) != NULL){
write(connfd,buf,strlen(buf)+1);
printf("client send:%s\n",buf);
bzero(buf,BUFSIZ);
if(-1 == read(connfd,buf,BUFSIZ)){
perror("read err");
return 1;
}
printf("client recv:%s\n",buf);
}
close(connfd);
}
基本概念
套接字
socket是一个编程接口,是一种特殊的文件描述符,everything in unix is a file;
协议域:AF_INET表示IPv4;AF_INET6:表示IPv6;AF_LOCAL表示Unix域;
类型:流式套接字:SOCK_STREAM,用于表示提供面向连接、可靠地数据传输服务,数据按照字节流,按顺序的收发,保证在传输过程中无丢失,无冗余;TCP协议支持该套接字;数据报套接字:SOCK_DGRAM,提供面向无连接的服务,数据收发无序,不能保证数据的准确到达,UDP支持该套接字;原始套接字:SOCK_RAW,允许对于传输层的协议或者物理层的直接访问;经常用于检测新的协议;
协议控制:IPPROTO_TCP;IPPROTO_UDP;
套接字的五大要素:协议,本地地址,本地端口,远程地址,远程端口;
两种协议
TCP:Transaction Control Protocol:面向连接的数据流,需要经过三次握手,四次断开的过程,三次握手的过程是用来确定通信的双方都存在;在三次握手的过程中存在这几个标识:synchronous:表示用于建立连接;ACK:acknowledgedgement:用于确认连接;FIN:finish表示用于结束;PSH:push表适用于传送;RST:reset表适用于重置;URG:urgent:表示紧急指针;两个号码:Sequence number:顺序号码;acknowledge number:用于确认的号码;
TCP链接经常用于数据的完整性要求高,数据的可靠性要求高,数据的性能要求比较低的情况下;
UDP:User Datagram Protocol表示无连接的数据报服务,类比于发短信;
分类:单播:municast:表示私聊;
广播:broadcast:
发送者:打开socket-->打开广播-->设置发送地址和端口-->发送数据-->关闭socket;
1、打开socket:cfd = socket(AF_INET,SOCK_DGRAM,0);
2、打开广播:setsockopt(cfd,SOL_SOCKET,SO_BROADCAST,&n,sizeof(n));其中n:0表示关闭属性,非0表示打开属性;
3、设置发送地址和端口:struct sockaddr_in si;
si.sin_family = AF_INET;//表示用于设置协议族,一般使用的是AF_INET;
si.sin_port = htons(8080);//用于绑定并且设置端口;
si.sin_addr.s_addr = inet_addr(“255.255.255.255”);
4、发送数据:sendto(cfd,buffer,buffer_size,0,(struct sockaddr *)&si,sizeof(si));
5、关闭socket:close(cfd);
接受者:打开socket-->设置socket-->设置接受地址和端口-->端口绑定-->接收数据-->关闭socket;
1、打开socket:int cfd = socket(AF_INET,SOCK_DGRAM,0);
2、设置接受地址和端口:
struct sockaddr_in si;
si.sin_family = AF_INET;//一般使用AF_INET;
si.sin_port = htohs(8000);
si.sin_addr.si_addr = INADDR_ANY;//一般使用IP地址,用于表示接收任意IP、任意网卡的发送给指定端口 数据;
3、端口绑定:int ret = bind(cfd,(struct sockaddr *)&si,sizeof(si));
4、接收数据:recv(cfd,buf,22,0);
5、关闭socket:close(cfd);
多播/组播(multicast)
多播地址的范围是:IPv4,224.0.0.0-->239.255.255.255;
范围 | 名称 | 作用 |
---|---|---|
224.0.0.0-->224.0.0.255 | 链路组播地址 | 路哟协议和其他用途保留的地址 |
224.0.1.0-->238.255.255.255 | 用户组播地址 | 用于全球范围内或者网络协议 |
239.0.0.0-->239.255.255.255 | 本地组播地址 | 内部网络使用 |
TTL:Time To Live生存时间值,IP数据包北路由其丢弃之前允许通过的最大的网段数量,TTL的最大值是255,默认值是64,作用是避免IP包在网段中的无限循环和收发,节省了网络资源;UDP协议适用的场景:发送的数据少,连接的时间短,传输性能要求高,可靠性完整性要求低;
路径
IP地址表示的是Internet中主机的标识,32位的IPv4,128位的IPv6,表示形式IPv4使用点分十进制表示,IPv6使用点分十进制或者点分十六进制表示;通配地址:0.0.0.0表示主机上的所有IP地址,多个网卡可以共用;回环地址:127.0.0.1,本地虚拟接口,无网卡可用,用于检查本地网络协议;广播地址:255.255.255.255当前路由器均不允许转发此类广播;子网广播地址:xxx.xxx.xxx.255,全子网广播xxx.xxx.255.255
域名:DNS用于实现IP地址和域名之间的对应关系;
端口:端口是用来区分一台主机接收到的数据报应该交给那个进程来进行处理的;查看端口可以使用nestat -apn;
关于字节序
大端:big endian表示将高字节存储在起始地址;
小段:LE(Little Endian)表示将低字节存储在起始地址;
可以使用函数ntol来进行大小端的转换;