Linux网络编程I
1.网络结构模式
-
C/S结构(客户端/服务器)
-
服务器存有多个用户共享的信息与功能,执行后台服务,如控制共享数据库的操作等
-
客户端为用户专有,负责执行前台功能,包括出错提示、在线帮助等功能,并且可以在子程序间自由切换。
-
优点:1)能充分发挥客户端PC的处理能力,很多工作可以在客户端吃力后嫁给服务器,响应速度快;2)操作界面漂亮,个性化定制;3)管理信息系统具有较强的事务处理能力,能实现复杂的业务流程;4)安全性较高,面对固定用户群,对权限进行多层校验,提供了安全的存储模式。
-
缺点:1)需要安装客户端软件,维护升级成本高;2)操作系统有限制,不能跨平台。
-
B/S结构(浏览器/服务器)
-
WEB浏览器是客户端最主要的应用软件,这种模式统一了客户端,将系统功能实现的核心部分集中到服务器上,简化了系统的开发、维护和使用。
-
客户机上只要安装有浏览器,即可通过Web Server与服务器上的数据交互。
-
优点:成本低、维护方便、分布性强、开发简单。客户端零成本,系统的扩展容易。
-
缺点:通信开销大、系统和数据的安全性较难保障;协议是固定的http/https,无法操作大数据量文件;客户端服务器端的交互时请求-响应模式,动态刷新页面,响应速度低。
2.协议。网络协议,规定通信计算机双方必须遵从的一组规则,体现为在网络上传输的数据包的格式。协议往往分为几个层次定义,某一层协议的改变不影响其他层次的协议。
3.常见的协议:
-
应用层:文件传输协议FTP,HTTP协议,网络文件协议NFS。
-
传输层:TCP协议,UDP协议。
-
网络层协议:IP协议,网际控制报文协议ICMP,网络组报文协议IGMP,地址解析协议ARP。
4.UDP数据报首部
5.TCP报文段的首部
6.协议封装。上层协议如何使用下层协议提供的服务?
-
应用程序数据在发送到物理网络之前,将沿着协议栈从上往下依次传递。每层协议都在上层数据的基础上加上自己的头部信息和尾部信息,以实现该层的功能。
7.数据报分用。接收方计算机如何准确接受到信息?
-
当帧到达目的主机后,将沿着协议栈自底向上依次传递。各层协议依次处理下层协议传上来的数据,以获取所需要信息,并将最终处理后的数据交给目标程序。
-
分用依靠的是数据报头部信息中的类型字段实现的。
8.ARP地址解析协议。解决同一局域网上的主机/路由器的IP地址和硬件地址的映射问题。
-
每一台主机上都有ARP高速缓存,里面存有本局域网上各主机和路由器的IP地址到硬件地址的映射表。每个项目根据生存时间动态更新。
9.socket套接字。
-
网络中不同主机上的应用进程之间进行双向通信的端点的抽象。上联应用进程,下联网络协议栈,是应用进程通过网络协议进行通信的接口。
-
通信时,其中一个网络应用程序将要传输带一段信息写入它所在主机的socket中,该socket通过与网卡连接的传输介质将这段信息传送到另外一台主机的socket中,使对方能接收到这段信息。
-
socket由IP地址和端口结合,提供向应用进程传送数据包的机制。
-
socket表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件,因此可以使用文件描述符引用套接字。
-
与管道类似,Linux将其封装成文件可以统一接口,使得读写套接字和读写文件的操作一致。
10.字节序
-
小端字节序:数据的高位字节存储在内存的高位地址,低位字节存储在内存的低位地址。
-
大端字节序:数据的高位字节存储在内存的低位地址,低位字节存储在内存的高位地址。
11.字节序转换函数。网络字节序是TCP/IP中规定好的数据表示格式,采用大端排序方式。
#include <arpa/inet.h> uint16_t htons(uint16_t hostshort); // h: host 主机字节序 uint16_t htonl(uint16_t hostlong); // n: network 网络字节序 uint16_t ntohs(uint16_t hostshort); // s: short 转换端口 uint16_t ntohl(uint16_t hostlong); // l: long 转换ip
12.socket地址。所有专用socket地址类型的变量在实际使用时都需要转化成通用socket地址类型sockaddr(强制转换),因为所有socket编程接口使用的地址参数类型都是sockaddr。
# 通用socket地址 #include<bits/socket.h> strcuct sockaddr { sa_family_t sa_family; // 地址族,对应协议族,PF_UNIX, AF_UNIX: Unix本地域协议族;PF_INET, AF_INET: TCP/IPv4协议族;PF_INET6, AF_INET6: TCP/IPv6协议族 char sa_data[14]; // socket地址 }; struct sockaddr_storage { sa_family_t sa_family; unsigned long int __ss_align; char __ss_padding[128 - sizeof(__aa_align)]; }; typedef unsigned short int sa_family_t; # 专用socket地址 #include <netinet/in.h> struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; // 端口 struct in_addr sin_addr; // ip unsigned char sin_zero[sizeof(struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof(in_port_t_ - sizeof(struct in_addr)]; }; struct in_addr { in_addr_t s_addr; }; typedef unsigned short uint16_t; // 2字节 typedef unsigned int uint32_t; // 4字节 typedef uint16_t in_port_t; typedef uint32_t in_addr_t;
13.IP地址转换。(字符串ip - 整数,主机、网络字节序的转换)
# 点分十进制字符串表示的IPv4地址和用网络字节序整数标表示的IPv4地址之间的转换 #include <arpa/inet.h> int inet_pton(int af, const char *src, void *dst); // p: point 点分十进制的IP字符串,n: network 网络字节序的整数 - af: 地址族 AF_INET AF_INET6 - src: 需要转换的点分十进制的IP字符串 - dst: 转换后的结果,可以用int类型保存,正好4个字节 const char *inet_ntop(int af, const void *src, char *dst, socken_t size); - af: 地址族 AF_INET AF_INET6 - src: 需要转换的ip的整数的地址 - dst: 转换成IP地址字符串保存的地方 char ip[16]; - size: 第三个参数的大小(数组的大小) - 返回值:返回转换后的数据的地址,和dst一样
14.TCP通信流程。
// 服务器端(被动接受连接的角色)
1. 创建一个用于监听的套接字(文件描述符)
2. 将这个监听文件描述符和本地的IP和端口绑定(IP和端口就是服务器的地址信息)
- 客户端连接服务器端的时候用的就是这个IP和端口
3. 设置监听,监听的fd开始工作
4. 阻塞等待,当有客户端发起连接,解除阻塞,接收客户端的连接,会得到一个和客户端通信的套接字(新的fd)
5. 通信
- 接收数据
- 发送数据
6. 通信结束,断开连接
// 客户端
1. 创建用于连接的套接字(fd)
2. 连接服务器,需要指定连接服务器的IP和端口
3. 连接成功,客户端可以直接和服务器通信
- 接收数据
- 发送数据
4. 通信结束,断开连接
15.套接字函数
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> int socket(int domain, int type, int protocol); - 作用:创建一个套接字 - domain: 协议族,F_UNIX 本地套接字通信;AF_INET IPv4;AF_INET6 IPv6 - type: 通信过程中使用的协议类型,SOCK_STREAM: 流式协议;SOCK_DGRAM: 报式协议 - protocol: 具体的协议,一般为0,SOCK_STREAM: 流式协议默认使用TCP;SOCK_DGRAM: 报式协议默认使用UDP - 返回值:成功返回文件描述符,操作单是内核缓冲区;失败返回-1并设置errno int bind(int sockfd, const struct *sockaddr, socklen_t *addrlen); - 作用:绑定,将fd和本地的IP和端口进行绑定 - sockfd: 通过socket函数得到的文件描述符 - addr: 需要绑定的socket地址,这个地址封装了ip和端口号的信息 - addrlen: 第二个参数结构体占的内存大小 - 返回值:成功返回文件描述符,操作的是内核缓冲区;失败返回-1并设置errno int listen(int sockfd, int backlog); - 作用: 监听这个socket上的连接 - sockfd: 通过socket函数得到的文件描述符 - backlog: 未连接和已连接的和的最大值,有未连接和已连接的2个队列,一旦accept()之后,将从已连接队列中剔除? - 返回值:成功返回文件描述符,操作单是内核缓冲区;失败返回-1并设置errno int accept(int sockfd, const struct *sockaddr, socklen_t *addrlen); - 作用:接收客户端连接,默认是阻塞函数,阻塞等待客户端的连接 - sockfd: 用于监听的文件描述符 - addr: 传出参数,记录连接成功后的客户端的地址信息(ip,port) - addrlen: 第二个参数占的内存大小 - 返回值:成功返回用于通信的文件描述符;失败返回-1并设置errno int connect(int sockfd, const struct *sockaddr, socklen_t addrlen); - 作用:客户端连接服务器 - sockfd: 用于通信的文件描述符 - addr: 客户端要连接的服务器的地址信息 - addrlen: 第二个参数占的内存大小 - 返回值:成功返回0;失败返回-1并设置errno ssize_t write(int fd, const void *buf, size_t count); ssize_t read(int fd, void *buf, size_t count);
16.滑动窗口理解。
-
窗口理解为缓冲区大小
-
滑动窗口的大小会随着发送和接收数据而变化
-
通信双方都有发送缓冲区和接收缓冲区
发送方的缓冲区:
白色格子:空闲的空间
灰色格子:数据已经发送,但还是未被接收
紫色格子:还没有发送出去的数据
接收方的缓冲区:
白色格子:还可以接收数据的空闲空间
紫色格子:已接收的数据
17.TCP三次握手和四次挥手。
-
建立连接三次握手,断开连接四次挥手。
-
标志位SYN=1和FIN=1,要消耗一个序号。
-
四次挥手发生在断开连接时,在程序中调用close()会使用TCP协议进行4次挥手。客户端和服务器端都可以主动发起断开连接,谁先调用close()谁就是发起。