目录
一、网络编程
1、协议
一组规则。
2、分层模型结构
OSI七层模型:物、数、网、传、会、表、应
物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
TCP/IP 4层模型:
网络接口层(链路层)、网络层、传输层、应用层(网网传应)
应用层:http、ftp、nfs、ssh、telnet。。。
传输层:tcp、UDP。。
网络层:IP、ICMP、IGMP
链路层:以太网帧协议、ARP
3、c/s模型: client-server
优点:缓存大量数据,协议选择灵活,速度快,
缺点:安全性,跨平台,开发工作量
4、b/s模型:browser-server
优点:安全性,跨平台,开发工作量较小
缺点:不能缓存大量数据、严格遵守http协议
5、网络传输流程:
数据没有封装之前,是不能再网络中传递。
6、以太网帧协议:
ARP协议:根据Ip地址获取mac地址
以太网帧协议:根据mac地址,完成数据包传输
7、IP协议
版本:IPv4、IPv6----4位
TTL:time to live。设置数据包在路由节点中的跳转上限。每经过一个路由节点,该值-1。当减为0的路由,有义务将该数据包丢弃
源IP:32位-----4字节 192.168.1.108 ----点分十进制 IP地址(string)---二进制
目的IP:32位-----4字节
· IP地址: 可以在网络环境中,唯一标识一台主机
端口号:可以在网络的一台主机上,唯一标识一台进程
IP地址+端口号:可以在网络环境中,唯一标识一台进程
8、UDP
16位:源端口号, 2^16=65536
16位:目的端口号
9、IP协议:
16位:源端口号, 2^16=65536
16位:目的端口号
32序号:
32确认序号:
6个标志位
16位窗口大小 2^16=65536
10、网络套接字 socket
一个文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现)
在通信过程中,套接字一定是成对出现的
11、网络字节序
小端法:(pc本地存储) 高位高地址,低位存低地址。 int a=0x12345678
大端法:(网络存储) 高位低地址,低位存高地址
htonl --》本地 --》网络(IP)
htons --》本地 --》网络(port)
ntohl --》网络 --》本地(IP)
ntohs --》网络 --》本地(port)
12、IP地址转换函数
int inet_pton(int af, const char *src, void *dst) 本地字节序(string Ip)--》网络字节序
af:AF_INET、AF_INER6(指代选择什么协议)
src:传入参数,IP地址(点分十进制)
dst:传输参数,转换后的网络字节序的IP地址
返回值:
成功:返回1
异常:0,说明src指向的不是一个有效的IP地址
const char *inet_ntop(int af, const void *src, char *dst,socklen_t size)网络字节序--》本地(string Ip)
af:AF_INET、AF_INER6(指代选择什么协议)
src:网络字节序IP地址(点分十进制)
dst:转换后的本地字节序的IP地址
size:dst的大小
返回值:
成功:返回dst
失败:NULL
13、sockaddr地址结构
struct sockaddr_in addr;
addr.sin_family=AF_INET/AF_INET6
addr.sin_port=htons(9527)
1、 int dst;
inet_pton(AF_INET, "192.157.22.45", &dst);
addr.sin_addr.s_addr=dst;
2、addr.sin_addr.s_addr=htonl(INADDR_ANY); //自动取出系统中有效的任意IP地址。二进制类型
bind(fd,(struct sockaddr *)addr,size)
14、网络套接字函数
socket模型创建流程图
bind():绑定IP+port
listen():设置监听上限
accept():阻塞直到客户端连接
connect():绑定IP+port
(1)socket函数
#include <sys/socket.h>
int socket(int domain, int type, int protocol) 创建一个套接字
domain:所选用的IP地址协议是什么AF_INET,AF_INET6,AF_UNIX
type:创建套接字所选用的数据传输协议默认是什么:SOCK_STREAM、SOCK_DGRAM
protocol:0
返回值:
成功:新套接字所对应的文件描述符
失败:-1 errno
(2)int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)给socket绑定一个地址结构(IP+port)
sockfd:socket返回值
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(8888);
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr:(struct sockaddr *)&addr
addrlen:sizoef(addr) 地址结构的大小
返回值:
成功:0
失败:-1,errno
(3)int listen(int sockfd, int backlog); 设置同时与服务器建立连接的上限数(可以同时进行3次握手的客户端数量)
sockfd:socket返回值
backlog:上限数值.最大值为128
返回值:
成功:0
失败:-1,errno
(4)int accept(int sockfd, struct sockaddr *addr, socklen_t addrlen)阻塞等待客户端建立连接,成功的话,返回一个与客户端成功连接的socket的文件描述符
sockfd:socket返回值
addr:传出参数。成功与服务器建立连接的那个客户端的地址结构(IP+port)
addrlen:传入传出参数 入:addr的大小 出:客户端addr的实际大小
返回值:能与服务器进行数据通信的socket对应的文件描述
(5)int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen)使用现有的socket 与服务器建立连接
sockfd:socket返回值
addr:传出参数。服务器的地址结构
socklen_t addrlen:服务器的地址结构大小
返回值:
成功:0
失败:-1,errno
如果不适用bind()绑定客户端地址结构,采用“隐式绑定”
15、TCP通信流程分析
server:
(1)socket():创建socket
(2)bind():绑定服务器地址结构
(3)listen():设置监听上限
(4)accept():阻塞监听客户端连接
(5)read():读socket获取客户端数据
(6)小写--》大写:toupper()
(7)write(fd)
(8)close();
client:
(1)socket():创建socket
(2)connect():与服务器建立连接
(3)write():写数据到socket
(4)read():读转换后的数据
(5)显示读取结果
(6)close();
16、三次握手协议
主动发起连接请求端,发送SYN标志位,请求建立连接。携带序号、数据字节数(0)、滑动窗口大小
被动接受连接请求端,发送ACK标志位,同时携带SYN请求标志位。携带序号、确认序号、数据字节数(0)、滑动窗口大小
主动发起连接请求端,发送ACK标志位,应答服务器请求连接。携带确认序号。
17、四次挥手
主动关闭连接请求端,发送FIN标志位
被动关闭连接请求端,应答ACK标志位 ------半关闭完成
被动关闭连接请求端,发送FIN标志位
主动关闭连接请求端,应答ACK标志位 ------连接全部关闭
18、滑动窗口
发送给连接对端,本端的缓存区大小(实时),保证数据不会丢失
19、错误处理函数:
封装目的:在server.c编程过程中突出逻辑,将出错处理与逻辑分开,可以直接跳转man手册。
(1)wrap.c
存放网络通信相关常用 自定义函数
命名方式:系统调用函数首字符大写,方便查看man手册。如:listen(),accept()
函数功能:调用系统调用函数,处理出错场景
在server.c和client.c中调用自定义函数
联合编译 server.c和wrap.c生成server
client.c和wrap.c生成client
(2)wrap.h
存放 网络通信相关应用 自定义函数原型(声明)
20、readn、readline:读N个字节,读一行
read函数的返回值:
(1)>0 实际读到的字节数
(2)=0 已经读到结尾(对端已经关闭)
(3)-1 应进一步判断errno的值
errno = EAGAIN or EWOULDBLOCK :设置了非阻塞方式 读:没有数据到达
errno = EINTR 慢速系统调用被中断
errno = “其他情况” 异常
21、多进程并发服务器
(1)socket(); 创建 监听套接字lfd
(2)bind(); 绑定地址结构struct socketaddr_in addr;
(3)listen();
(4)while(1){
cfd=Accept();接收客户端连接请求
pid=fork();
if(pid==0){ 子进程 read