1.TCP和UDP区别
都为传输层协议
UDP:用户数据报协议,面向无连接,可以单播,多播,广播,面向数据报,不可靠
TCP:传输控制协议,面向连接的,可靠的,基于字节流,仅支持单播传输
UDP | TCP | |
---|---|---|
是否创建连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠 | 可靠的 |
连接的对象个数 | 一对一、一对多、多对一、多对多 | 一对一 |
传输的方式 | 面向数据报 | 面向字节流 |
首部开销 | 8个字节 | 最少20个字节 |
适用场景 | 实时应用(视频会议,直播) | 可靠性高的应用 |
2.TCP通信流程
(1) 服务器端
1.创建一个用于监听的套接字
-监听:监听有客户端的连接
-套接字:就是一个文件描述符
2.将这个监听文件描述符和本地的IP和端口绑定(IP和端口就是服务器的地址信息)
-客户端连接服务器的时候使用的就是这个IP和端口
3.设置监听,监听的fd开始工作
4.阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个和客户端通信的套接字(fd)
5.通信
-接收数据
-发送数据
6.通信结束,断开连接
(2) 客户端
1.创建一个用于通信的套接字(fd)
2.连接服务器,需要指定连接的服务器的IP和端口3.连接成功了,客户端可以直接和服务器通信
-接收数据
-发送数据
4.通信结束,断开连接
3.通信时所需要用到的函数(API)
cpp
int socket(int domain,int type,int protoco1);
-功能:创建一个套接字
-参数:
- domain : 协议族
AF_INET : ipv4
AF_INET6 : ipv6
AF__UNIX,AF_LOCAL :本地套接字通信(进程间通信>
- type : 通信过程中使用的协议类型
SOCK_STREAM ︰流式协议
SOCK_DGRAM︰报式协议
-protoco1 : 具体的一个协议。一般写0
- SOCK_STREAM︰流式协议默认使用 TCP
- SOCK_DGRAM︰报式协议默认使用UDP
-返回值:
-成功:返回文件描述符,操作的就是内核缓冲区。-失败:-1
int bind(int sockfd,const struct sockaddr *addr,socklen_t addr len);
-功能:绑定,将fd 和本地的IP +端口进行绑定
-参数:
- sockfd :通过socketi函数得到的文件描述符
- addr :需要绑定的socket地址,这个地址封装了ip和端口号的信息
- addrlen :第二个参数结构体占的内存大小
int listen(int sockfd,int backlog); // /proc/sys/net/core/somaxconn
-功能:监听这个socket上的连接
-参数:
- sockfd :通过socket()函数得到的文件描述符
- backlog :未连接的和已经连接的和的最大值,5
int accept(int sockfd,struct sockaddr *addr ,socklen_t *addrlen);
-功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接
-参数:
- sockfd :用于监听的文件描述符
- addr :传出参数,记录了连接成功后客户端的地址信息(ip, port)
- addrlen :指定第二个参数的对应的内存大小
-返回值:
-成功:用于通信的文件描述符
- -1 :失败
int connect(int sockfd,const struct sockaddr *addr, socklen_t addrlen) ;
-功能:客户端连接服务器
-参数:
- sockfd :用于通信的文件描述符
- addr :客户端要连接的服务器的地址信息
- addrlen :第二个参数的内存大小
-返回值: 成功0,失败-1
ssize_t write(int fd,const void *buf,size_t count);//写数据
ssize_t read(int fd,void *buf, size_t count);//读数据
4.TCP三次握手
目的 :保证双方互相之间建立了连接
什么时候三次握手 :发生在客户端连接时
为甚莫要三次握手 :确认双方都能发送和接收数据
三次握手在TCP协议头中 :1.将SYN的值置为1 。2.服务端将ACK置为1,并且将SYN置为1 。3.客户端将ACK置为1
序号,确认号:确保TCP协议的传输时的完整性和顺序性
cpp
第一次握手:
1.客户端将SYN标志位置为1
2.生成一个随机的32位的序号seq=J ,这个序号后边是可以携带数据(数据的大小)
第二次握手:
1.服务器端接收客户端的连接:ACK=1
2.服务器会回发一个确认序号:ack=客户端的序号+数据长度+SYN/FIN(按一个字节算)
3.服务器端会向客户端发起连接请求:SYN=1
4.服务器会生成一个随机序号:seq = K
第三次握手:
1.客户单应答服务器的连接请求:ACK=1
2.客户端回复收到了服务器端的数据:ack=服务端的序号+数据长度+SYN/FIN(按一个字节算)
5.三次握手、滑动窗口、四次挥手
c
# mss: Maximum Segment Size(一条数据的最大的数据量)
# win: 滑动窗口
1. 客户端向服务器发起连接,客户单的滑动窗口是4096,一次发送的最大数据量是1460
2. 服务器接收连接情况,告诉客户端服务器的窗口大小是6144,一次发送的最大数据量是1024
3. 第三次握手
4. 4-9 客户端连续给服务器发送了6k的数据,每次发送1k
5. 第10次,服务器告诉客户端:发送的6k数据以及接收到,存储在缓冲区中,缓冲区数据已经处理了2k,窗口大小是2k
6. 第11次,服务器告诉客户端:发送的6k数据以及接收到,存储在缓冲区中,缓冲区数据已经处理了4k,窗口大小是4k
7. 第12次,客户端给服务器发送了1k的数据
8. 第13次,客户端主动请求和服务器断开连接,并且给服务器发送了1k的数据
9. 第14次,服务器回复ACK 8194, a:同意断开连接的请求 b:告诉客户端已经接受到方才发的2k的数据c:滑动窗口2k
10.第15、16次,通知客户端滑动窗口的大小
11.第17次,第三次挥手,服务器端给客户端发送FIN,请求断开连接
12.第18次,第四次回收,客户端同意了服务器端的断开请求
6.TCP三次握手、四次挥手的状态转换
Client | Server |
---|---|
SYN_SENT(connect()) | LISTEN(listen()) |
SYN_RECVD | |
ESTABLISHED | ESTABLISHED |
Client | Server |
---|---|
FIN_WAIT_1 (close()) | CLOSE_WAIT |
LAST_ACK(close()) | |
FIN_WAIT_2 | |
TIME_WAIT |
主动断开连接的一方, 最后进入一个 TIME_WAIT状态, 这个状态会持续: 2msl
半关闭状态
7.端口复用
在最后的关闭时,出现TIME_WAIT状态,在此调用时,会出现端口占用的错误。所用要用端口复用
端口复用最常用的用途是:
防止服务器重启时之前绑定的端口还未释放
程序突然退出而系统没有释放端口
c
#include <sys/types.h>
#include <sys/socket.h>
// 设置套接字的属性(不仅仅能设置端口复用)
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_toptlen);
参数:
- sockfd : 要操作的文件描述符
- level : 级别 - SOL_SOCKET (端口复用的级别)
- optname : 选项的名称
- SO_REUSEADDR
- SO_REUSEPORT
- optval : 端口复用的值(整形)
- 1 : 可以复用
- 0 : 不可以复用
- optlen : optval参数的大小
端口复用,设置的时机是在服务器绑定端口之前。
setsockopt();
bind();