文章目录
一、初始协议
1、理解
- 协议是一种约定。(二进制)
举例子:打电话约定电话铃响的次数(响一声是…、响两声是…)
- 不过光是这样是不够的,还需要更加细致的规定。(语言)
举例子:同样是1 + 1 = 2, 在不同的国家之间,因为语言的不同,表达方式可能不同。
定协议的人和实现的人可能是两批人。
2、协议分层
协议本身也是软件,是层状结构。
3、软件分层
分层可以实现解耦合,让软件的成本更低。
4、OSI七层模型
会话层、表示层、应用层、传输层、网络层、数据链路层、物理层
5、TCP/IP五层模型
应用层、传输层、网络层、数据链路层、物理层
二、再识协议
1、为什么要有TCP/IP协议
本质就是通信主机的距离变远了。
2、什么是TCP/IP协议
TCP(传输层)IP(网络层)
计算机内部都是存在协议的。
TCP/IP是一种解决方案。
TCP/IP协议能分层是因为问题的本身就是分层的。
3、TCP/IP协议与操作系统的关系(宏观上,怎么实现的)
网络协议栈必须相同。
所谓协议, 就是通信双方都认识的结构化的数据类型。
因为协议栈是分层的, 所以, 每层都有双方都有协议, 同层之间, 互相可以认识对方的协议。
三、网络传输基本流程
1、mac地址
唯一标识主机:mac地址。
mac地址用来标识数据链路层中相连的节点。
mac地址在网卡出厂的时候就确认了,不能修改,mac地址通常是唯一。
在以太网中,任何时刻,只允许一台机器向网络中发送数据。
如果有多态同时发送,会发生数据干扰,我们成为数据碰撞。
所有发送数据的主机要继承碰撞检测和碰撞检测。
在没有交换机的情况下,一个以太网就是一个局域网。
局域网通信的过程中, 主机对收到的报文确认是否是发给自己的, 是通过目标mac 地址判定。
2、TCP/IP协议通信的过程
- 报文 = 报头 + 有效载荷。—> 栈结构
- 数据在网络中发送的时候,一定最终要在硬件上跑。
网络协议的共性:
1、报头和有效载荷分离的问题 — 解包
2、除了应用层,每一层协议,都必须解决一个问题,自己的有效载荷,应该要交给上层的哪一种协议 — 分用。
3、认识IP地址
- IP地址是在IP协议中,用来标识不同主机的地址。(IPv4,32位,4字节的整数)
- IP地址用点分十进制表示。
- 网络层(就是IP层)向上(包括网络层)看到的所有的报文都是一样的,都至少是IP报文。
- IP可以屏蔽底层网络的差异!
- 所有的网络都是IP网络。
IP地址 vs Mac地址
IP地址是报文的目的地。
Mac地址是报文的途径地。
网络通信的宏观路径
四、Socket编程
1、理解IP和Mac
Mac地址只在局域网内有效,标识局域网中主机的唯一性。
IP在网络中,用来标识主机的唯一性。
数据传输到主机不是目的,而是手段。到达主机内部,再交给主机内的进程才是目的。
传输层协议(TCP、UDP)中源端口、目的端口,描述数据是谁发的,要发给谁。
2、端口号Port
- 端口号是一个2字节,16位的整数。用来表示一个主机内部的唯一进程。
- IP + Port = 网络中唯一的进程!
- 网络通信的本质就是进程间通信。
- 服务端启动的时候,就一定要和一个port关联起来。
进程已经有了唯一标识的Pid,为什么还要用端口号来进行唯一标识进程呢?
为了保证系统和网络进行解耦!当底层pid部分发生更改了,也不影响网络通信。
- 同主机内部:两个进程通信,要看到同一个资源,进程具有独立性。
- 不同主机内部:两个进程通信,看到同一个资源(网络),进程具有独立性。
进程都有pid,但是不是所有的进程都需要网络通信,只有需要网络通信的进程才会有端口号。
3、传输层代表
(1)、认识TCP协议
特点:
- 传输层协议
- 有连接
- 可靠传输
- 面向字节流
(2)、认识UDP协议
特点:
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
4、网络字节序
都知道计算机有大小端之分,那如何保证大小端之间的计算机之间能进行正常通信呢?
规定在网络中传输的数据以大端形式发送。
小端存储:小小小(小端存储、低地址、低字节)
大端存储:大大小(大端存储、高地址、低字节)
#include <arpa/inet.h>
// h--->host:本主机
// n--->net:网络
uint32_t htonl(uint32_t hostlong); // 将hostlong的32位无符号整数转化为网络字节序
uint32_t ntohl(uint32_t netlong); // 将netlong的32位无符号整数转化为主机字节序
uint16_t htons(uint16_t hostshort); // 将hostshort的16位无符号整数转化为网络字节序
uint16_t ntohs(uint16_t netshort); // 将netshort的16位无符号整数转化为主机字节序
5、socket编程接口
(1)Udp
创建套接字socket
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
绑定端口bind
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
// 返回值:成功返回0, 失败返回-1,设置errno
---------------------------------------------
#include <netinet/in.h>
struct sockaddr_in {
sa_family_t sin_family; // 地址族,通常是 AF_INET
uint16_t sin_port; // 端口号
struct in_addr sin_addr; // IP 地址
};
struct in_addr {
uint32_t s_addr; // IP 地址
};
// 填充sockaddr
struct sockaddr_in local;
memset(&local, 0, sizeof(local));//先置空
local.sin_family = AF_INET;
local.sin_port = htos(_localport);
//local.sin_addr.s_addr = inet_addr(_localip.c_str())// 需要4字节IP,需要网络序列的IP,不建议
inet_pton(AF_INET, _localip.c_str(), &local.in_addr); // 建议
// 将字符串IP地址转化为网络字节序
int inet_pton(int af, const char *src, void *dst);
in_addr_t inet_addr(const char *cp);
// 将网络字节序IP地址转化成字符串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
char *inet_ntoa(struct in_addr in);
接收消息recvfrom
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
发送消息sendto
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
(2)Tcp
socket,bind和Udp同理。
开始监听 socket (TCP, 服务器)listen
#include <sys/types.h>
#include <sys/socket.h>
int listen(int socket, int backlog);
// 这个backlog我设置为8
// 成功返回0,失败返回-1且错误码被设置
接收请求 (TCP, 服务器)accept
#include <sys/types.h>
#include <sys/socket.h>
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
建立连接 (TCP, 客户端)connet
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
(3)socketaddr结构
为了使用同意一套接口既能实现网络通信又能实现本主机通信,所以设计了一个socketaddr结构体,接口中使用socketaddr,但是传的是struct sockaddr_in、struct sockaddr_un。—这种结构,也就是C语言版的多态。
谢谢大家!