网络编程


网络编程

各层典型协议

网络接口与物理层

​ 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个文件描述符

文件描述符的特点:

  1. 非负整数
  2. 从最小可用的数字来分配
  3. 每个进程启动时默认打开0,1,2三个文件描述符

多路复用针对是不止是文件描述符fd,也包括普通文件描述符fd

流程

  1. 把关心的文件描述符加入到集合(fd_set:集合长度为maxfd+1,一般是4个字节的整数)中
  2. 调用select()/poll()函数去监控集合fd_set中哪些文件描述符(阻塞等待集合中一个或者多个文件描述符有数据)
  3. 当有数据时退出select()阻塞
  4. 依次判断哪个文件描述符有数据
  5. 依次处理有数据的文件描述符上的数据

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.如果是已建立连接的套接字则读数据

}


文章作者: hhs1231
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hhs1231 !
评论
  目录