网络
网络模型有哪两类?答:OSI、 TCP/IP
OSI有哪七层?答:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
端口号的作用?答:区别不同服务
TCP建立与关闭连接分别几次握手?答:3次 4次
总结
OSI模型
七层结构简介
应用OSI 模型
帧规范
一种典型的Ethernet帧
一种典型的Token Ring
传输控制协议/网际协议
TCP/IP模型和OSI的比较
相同点
- 两者都是以协议栈的概念为基础
- 协议栈中的协议彼此相互独立
- 下层对上层提供服务
不同点
- OSI是先有模型; TCP/IP是先有协议,后有模型
- OSI适用于各种协议栈; TCP/IP只适用于TCP/IP网络
- 层次数量不同
TCP/IP核心协议
IP协议
- 网际协议(IP)属于TCP/IP模型和互连网层,提供关于数据应如何传输以及传输到何处的信息。
- IP是一种使TCP/IP可用于网络连接的子协议,即TCP/IP可跨越多个局域网段或通过路由器跨越多种类型的网络。
- 数据帧的I P部分被称为一个IP数据报, IP数据报如同数据的封面,包含了路由器在子网中传输数据所必需的信息。
IP数据包
传输控制协议( TCP)协议
- 属于TCP/IP协议群中的传输层,提供可靠的数据传输服务。
- TCP是一种面向连接的子协议,意味着在该协议准备发送数据时,通信节点之间必须建立起一个连接。
- TCP协议位于IP子协议的上层,通过提供校验和、流控制及序列信息弥补I P协议可靠性的缺陷。
- 另一方面, T C P包括了可保证数据可靠性的几个组件。是一种可靠的、面向连接的、字节流协议。
- TCP提供的可靠性是利用一种称为“重传肯定确认”机制来实现的。
TCP数据包
在传输数据之前,建立对话的两个端点之间交换称为握手的控制信息。
- TCP通过在段头第4个字的标志字段中设置相应的位来表示一个段的控制功能。
- TCP有三个段要交换,故称为“三段试握手”。
- 当协作双方的模块结束数据传输时,它们就利用包含“无数据发送(FIN)”位的段来交换三段试握手信息,以关闭连接。
UDP (用户数据报协议)
- UDP是一种无连接的传输服务,它不保证数据包以正确的序列被接收
- 实际上根本不保证数据包的接收,而且,它不提供错误校验或序列编号。
- UDP报头仅包含了四个域:源端口、目标端口、长度和校验和。
TCP和UDP的区别
- TCP提供一种面向连接的、可靠的字节流服务
- UDP是无连接的、不可靠的数据协议报
字节序(大小端)
大端字节序: 低位存在高地址, 高位存在低地址
小端字节序: 低位存在低地址, 高位存在高地址
判断字节序
static int isBigEndian()
{
uint32_t thisx = 0x01020304;
uint8_t *thisp = (uint8_t *)&thisx;
return (*thisp == 1) ? 1 : 0;
}
int main(int argc, char **argv)
{
printf("Byte order: %s Endian\n", isBigEndian() ?"Big" :"Little");
return 0;
}
网络基础知识
本机端口号和网络端口相互转换
uint16_t htons(uint16_t hostshort);//本机 ---->网络
uint16_t ntohs(uint16_t netshort); // 网络 --->本机
//htonl为32位机
点分十进制IP 地址 和 网络地址相互转换
int inet_pton(int af, const char *src, void *dst);
函数功能: 将点分十进制地址 转成网络地址
参数说明:
af: 地址族
IPV4: AF_INET
IPV6: AF_INET6
src: ip地址内存的起始地址
dst: 存放网络地址内存的起始地址
const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);
函数功能:将网络地址 转成点分十进制地址 (字符串存储)
参数说明:
af: 地址族
src: 网络地址内存起始地址
dst: 存放点分十进制地址内存的起始地址
size : 内存的大小
例:inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
地址族结构体:
通过该结构体打包IP和端口号
原始结构体类型(已经不使用!):
struct sockaddr
{
sa_family_t sa_family; /* 地址族 */
char sa_data[14]; /* 地址值,实际可能更长 */
};
改进后的结构体:
struct in_addr
{
in_addr_t s_addr;
};
struct sockaddr_in
{
uint16_t sin_family; //地址族 IPv4: AF_INET
uint16_t sin_port; // 网络字节序端口号
struct in_addr sin_addr; // 网络字节序的IP地址
char sin_zero[8];
};
例:struct sockaddr_in seraddr;
int addrlen = sizeof(struct sockaddr_in);
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(8001);
inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
ret = bind(sockfd, (struct sockaddr*)&seraddr, addrlen);
TCP客户服务器编程模型
客户端通常的调用序列是:
- 调用socket函数创建套接字。
- 调用connect连接服务器端。
- 调用I/O函数(read/write)与服务器端通讯。
- 调用close关闭套接字。
服务器端通常的调用序列为:
- 调用socket函数创建套接字。
- 调用bind指定本地地址和端口。
- 调用listen启动监听。
- 调用accept从已连接列队中提取客户连接。
- 调用I/O函数(read/write)与客户端通讯。
- 调用close关闭连接
server服务器端
创建监听套接字
int socket(int domain, int type, int protocol);
参数说明:
domain : 地址族
IPV4: AF_INET
IPV6: AF_INET6
type : 协议类型
SOCK_STREAM :流式协议
SOCK_DGRAM :报文协议
protoal: 0
返回值: 成功返回新的套接字
失败返回-1
绑定地址和端口号
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数说明:
sockfd :监听文件描述符
addr : 打包网络字节序地址和端口号结构体变量的地址
addrlen: 结构体的长度
返回值:成功返回0, 失败返回-1
例:bind(sockfd, (struct sockaddr*)&seraddr, addrlen);
通知内核监听套接字
int listen(int sockfd, int backlog);
参数说明:
sockfd :监听文件描述符
backlog: 设置监听队列的长度
返回值:成功返回0, 失败返回-1
listen(sockfd, 10)
等待接收客户端
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数说明:
sockfd :监听文件描述符
addr :通用地址结构体变量地址, (保存请求连接客户端的IP + PORT)
addrlen: 保存结构体长度变量的地址
返回值: 成功返回新的套接字, 专门和对应的客户端通讯(全双工)
失败返回-1
ps : accept是一个阻塞函数, 如果没有客户端请求连接, 会一直阻塞等待, 一旦有客户端连接,解除阻塞,接收客户端
cfd = accept(sockfd, (struct sockaddr*)&cliaddr, &addrlen);
端口复用
int opt = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
client客户端
创建监听套接字
int socket(int domain, int type, int protocol);
参数说明:
domain : 地址族
IPV4: AF_INET
IPV6: AF_INET6
type : 协议类型
SOCK_STREAM :流式协议
SOCK_DGRAM :报文协议
protoal: 0
返回值: 成功返回新的套接字
失败返回-1
2、请求连接
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数说明:
sockfd : 套接字
addr : 存放服务器地址和端口号的地址族结构体变量地址
addrlen: 结构体长度
返回值: 成功返回0, 失败返回-1
例:ret = connect(sockfd, (struct sockaddr*)&seraddr, addrlen);
select函数
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
参数说明:
nfds: 最大文件描述符+1
readfds: 监听读集合地址
writefds: 监听写集合地址 NULL
exceptfds: 监听异常集合地址 NULL
timeout : 设置超时时间
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
返回值:
>0: 返回准备好的文件描述符个数
=0: 超时时间到了
-1: 错误
例:ret = select(maxfd+1, &rset, NULL, NULL, &tv);
辅助函数:
void FD_CLR(int fd, fd_set *set); //将fd从set集合中移除
int FD_ISSET(int fd, fd_set *set); //判断fd是否在set集合中,如果在返回真,否则返回假
void FD_SET(int fd, fd_set *set); // 将fd加入set集合中
void FD_ZERO(fd_set *set); // 清空set集合中的内容
epoll监听函数
创建集合空间
int epoll_create(int size);
参数:
size : 指定文件描述符的个数
返回值:
成功:返回与集合关联的文件描述符
失败: -1
2、管理集合空间中的文件描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数说明:
epfd:和集合关联的文件描述符
op : 命令参数
EPOLL_CTL_ADD: 往集合中添加文件描述符
EPOLL_CTL_MOD: 修改集合中文件描述符的信息
EPOLL_CTL_DEL: 删除集合中指定的文件描述符
fd: 操作的文件描述符
event: 如果是删除操作,该参数忽略,直接传NULL
如果是添加操作, 通过该参数告诉内核监听指定文件描述符的指定时间
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events: EPOLLIN(读事件)
3、监听集合中的文件描述符
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
参数说明:
epfd:和集合关联的文件描述符
events:存放准备好文件描述符信息数组的起始地址
maxevents: 数组的最大元素个数
timeout :设置超时时间 (以毫秒为单位的)
>0 : 指定超时时间
=0 : 非阻塞函数
-1 : 永久阻塞
返回值:
>0 : 准备好的文件描述符个数
=0 :超时时间到了
-1 :出错
#endif