网络编程
各层典型协议
网络接口与物理层
MAC地址:48位全球唯一,网络设备的身份标识
ARP/RARP
ARP:IP地址—->MAC地址
RARP:MAC地址—->IP地址
PPP协议
拨号协议(GPRS/3G/4G)
网络层(IP层)
IP: Internet protocol(IPV4/IPV6)
ICMP:Internet控制管理协议,ping命令属于ICMP
IGMP:Internet分组管理协议,广播,组播
传输层
TCP: Transfer Control protocol传输控制协议,提供面向连接的一对一可靠数据传输协议
UDP: user Datagram Protocol用户数据报协议,提供不可靠无连接的尽力传输协议
SCTP可靠传输,TCP增强版,可实现多主机多链路通信
应用层
网络访问协议HTTP/HTTPS
邮件发送接收协议POP3(收)/SMTP(发)、IMAP(可接收邮件的一种协议)
FTP
Telnet/SSH远程登录
嵌入式相关
NTP网络时钟协议
SNMP简单网络管理协议(实现对网络设备的集中式管理)
RTP/ETSP传输音视频协议(安防监控)
网络封包拆包
MTU:Max Transfer Unit最大传输单元,以太网中最大为1500
MSS:Maxium Segment Size和网络类型、线路、系统特性相关
Socket
编程的接口,是一种特殊的文件描述符(对它执行IO操作的函数,read()、write()、close()等操作函数)
代表网络编程的资源
socket类型
流式套接字 SOCK_STREAM
提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据淹没慢的接收方。数据被看作是字节流,五长度限制
数据报套接字 SOCK_DGRAM
提供无连接服务。数据包以独立数据包形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收
原始套接字 SOCK_RAW
可以对较低层次协议如IP、ICMP等直接访问
IP地址
IPV4:32为整数
IPV6:128位整数
mobileIPV6:local IP本地注册IP,roam IP漫游IP
IPV4地址:点分形式的32位整数
特殊IP地址
局域网IP:192.XXX.XXX.XXX 10.XXX.XXX.XXX
广播IP:XXX.XXX.XXX.255 255.255.255.255全网广播
组播IP:224.XXX.XXX.XXX-239.XXX.XXX.XXX不含广播IP
端口号
16位数字(1-65535)
常见端口:1-1023(FTP:21, SSH:22, HTTP:80, HTTPS:469)
保留端口:1024-5000(不建议使用)
可用:5001-65535
TCP端口和UDP端口相互独立
网络中通信为IP+端口号决定
字节序
不同CPU访问内存中多字节数据的时候存在大小端问题,如CPU访问字符串数据不存在大小端问题
低对低:小端
低端内存存放低端数据
低对高:大端
低端内存存放高端数据
一般X86/ARM:小端;powerpc/mips,ARM作为路由器:大端
网络传输:大端
主机字节序转网络字节序
u_long htonl(u_long hostlong);
u_short htons(u_short short);
网络字节序转主机字节序
u_long ntohl(u_long hostlong);
u_short ntohs(u_short short);
IP地址转换函数
in_addr_t inet_addr(const char *cp);
cp:点分形式的IP地址,结果是32位整数(内部包含了字节序的转换,默认是网络字节序的模式)
仅适用于IPV4,当出错时返回-1,此函数不能用于255.255.255.255的转换
inet_pton()/inet_ntop()
需要引用arpa/inet.h
int inet_pton(int af, const char *src, void *dst);
适用于IPV4和IPV6,能正确处理255.255.255.255的转换
af:地址协议族(AF_INET或AF_INET6)
src:是一个指针(填写点分形式的IP地址[主要指IPV4])
dst:转换的结果给到dst
成功为1,失败为0
const char *inet_ntop(int af, const char *src, void *dst);
适用于IPV4和IPV6,能正确处理-1的转换
af:地址协议族(AF_INET或AF_INET6)
src:是一个指针(32位网络字节序的IP地址)
dst:输出结果为点分形式的IP地址[主要指IPV4]
成功返回非空指针,失败返回空指针
TCP编程API
socket()创建函数
需要引用sys/types.h/sys/socket.h
int socket(int domain, int type, int protocol);
domain:
AF_INET IPV4 Internet protocol
AF_INET6 IPV6 Internet protocol
AF_UNIX, AF_LOCAL Local communication(本地属性)
AF_NETLINK Kernel user interface device(内核和用户通信)
AF_PACKET Low level packet interface
type:
SOCK_STREAM:流式套接字,唯一对应TCP
SOCK_DGRAM:数据报套接字,唯一对应UDP
SOCK_RAW:原始套接字
protocol:一般填0,原始套接字编程时需填充
成功返回文件描述符,失败返回-1
bind()绑定函数
需要引用sys/types.h/sys/socket.h
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:通过socket()函数拿到的fd
addr:struct sockaddr的结构体变量的地址(IPV4:sockaddr_in(man 7 ipv4),IPV6:sockaddr_in6(man 7 ipv6),通常更通用的方法可以通过struct sockaddr_storage来编程)
addrlen:地址长度
成功返回0,失败返回-1
listen()主动转被动函数
需要引用sys/types.h/sys/socket.h
int listen(int sockfd, int backlog);
backlog:一般填5(同时允许几路客户端和服务器进行连接,ARM最大为8)
成功返回0,失败返回-1
accept()阻塞等待客户端连接请求函数
需要引用sys/types.h/sys/socket.h
int accept(int sockfd, struct sockaddr *addr, socklen_t addrlen);
sockfd:经过前面socket()创建并通过bind(),listen()设置过的fd
addr和addrlen:可获取客户端的IP+端口号
成功返回已经建立好新的newfd,失败返回-1
connect()客户端连接函数
需要引用sys/types.h/sys/socket.h
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
和bind()函数类似
成功返回0,失败返回-1
INADDR_ANY可以获取程序运行所在主机任意网卡传输过来的数据
send()/write()网络发送数据
需要引用sys/types.h和sys/socket.h
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
需要引用unisd.h
ssize_t write(int fd, const void *buf, size_t count);
send()比write()多一个参数flags:一般填写0(阻塞),此时和write()作用一样
MSG_DONTWAIT:非阻塞版本
MSG_OOB:用于发送TCP类型的带外数据(out-of-band)
recv()/read()网络接收数据
需要引用sys/types.h和sys/socket.h
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
需要引用unisd.h
ssize_t read(int fd, void *buf, size_t count);
recv()比read()多一个参数flags:一般填写0(阻塞),此时和write()作用一样
MSG_DONTWAIT:非阻塞版本
MSG_OOB:用于发送TCP类型的带外数据(out-of-band)
MSG_PEEK:读完部分数据后续依旧从头开始读
阻塞相关函数
ioctl(int fd, int cmd, long arg);
cmd:和IO相关的是FIONBIO,可决定文件描述符是否为阻塞IO
arg:非零整数即为非阻塞IO
int fcntl(int fd, int cmd, long arg);
cmd:为F_GETFL时是获取文件描述符的标志,F_SETFL是设置文件描述符的标志
函数的结果|=O_NONBLOCK即可设置为非阻塞IO
多路复用
基本常识:
linux中每个进程默认情况下,最多可打开1024个文件,最多有1024个文件描述符
文件描述符的特点:
- 非负整数
- 从最小可用的数字来分配
- 每个进程启动时默认打开0,1,2三个文件描述符
多路复用针对是不止是文件描述符fd,也包括普通文件描述符fd
流程
- 把关心的文件描述符加入到集合(fd_set:集合长度为maxfd+1,一般是4个字节的整数)中
- 调用select()/poll()函数去监控集合fd_set中哪些文件描述符(阻塞等待集合中一个或者多个文件描述符有数据)
- 当有数据时退出select()阻塞
- 依次判断哪个文件描述符有数据
- 依次处理有数据的文件描述符上的数据
fd_set有关的函数
1.清零集合
void FD_ZERO(fd_set *fdset)
2.把fd加入集合
void FD_SET(int fd, fd_set *fdset)
3.从集合清除fd
void FD_CLR(int fd, fd_set *fdset)
4.判断fd是否在集合中
int FD_ISSET(int fd, fd_set *fdset)
阻塞函数
int select(int nfds, fd_set *readfds, fd_set, *writefds, fd_set *exceptfds, struct timeval *timeout)
nfds:fd_set集合的长度即maxfd+1
readfds:读集合
writefds:写集合,一般为NULL
exceptfds:异常集合,一般填NULL,除非有带外数据
timeout:超时阻塞时间
select()退出后集合中全是有数据的集合
struct timeval{
long tv_sec;//秒
long tv_usec;//微秒
}
1秒(s)=10^3毫秒(ms)=10^6微秒(us)=10^9纳秒(ns)=10^12纳秒(ps)
if(FD_SET(fd, rset)){
1.如果此fd为监听套接字则代表有新的客户端连接,则accept()接收
2.如果是已建立连接的套接字则读数据
}