TCP/TP协议是什么?
TCP/IP协议是一种四层协议,完整叫法是TCP/UDP/IP协议。
UDP 协议 (User Datagram Protocol 用户数据报协议),是一种保护消息边界的,不保障可靠数据的传输。
TCP 协议 (Transmission Control Protocol 传输控制协议),是一种流传输,无保护信息边界的协议。他提供可靠的、有序的、双向的、面向连接的传输。
保护信息边界 :传输协议把数据当作 一条独立的信息 在网上传输,接收端只能接受独立的信息。
无保护信息边界 :发送端连续发送数据,接收端可能在一次接受动作中,接受多个数据包。
Socket
介绍(与TCP/TP的关系)
Windows Socket是一个网络编程接口,它并非一个协议,而是一些协议的封装。
WinSock与协议基本上无关,这样可以保证其独立性,能够调用多种不同协议的功能。
WinSock可以是TCP/IP协议的一种封装,通过调用WinSock的接口函数来调用TCP/IP的各种功能。
基本使用流程
WinSock编程分为服务器端和客户端两部分。
接下来直接以TCP/IP为例来介绍其基本使用流程。
1.初始化
初始化WinSock DLL库:
cpp
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib") //告诉链接器链接 WinSock 库
WSADATA wsaData; //保存 WinSock 版本
int ret = WSAStarup(MAKEWORD(2,2),&wsaData); //申请网络资源,指定使用版本
if(ret != 0){
printf("初始化失败")
}
如果不调用WSAStartup,后面所有socket API都会失败。
2.创建Socket
基本格式:
cpp
SOCKET serverSocket = socket(
AD_INTE, // 使用IPv4协议
SOCK_STREAM, // 面向连接(TCP)
IPPROTO_TCP // 说明使用 TCP 协议
);
if(serverSocket == INVALID_SOCKET){
printf("创建失败");
}
3.设置通信地址(IP 与 端口)
基本格式:
SOCKET Socket(int af,int type,int protocol)
包含了两个信息,一个是 IP 地址,一个是 Port 端口号,使用这两个信息,就可以确定网络中的任何一个通讯节点。
cpp
sockaddr_in addr{}; // 这个是IPv4专用地址结构体,内部包含 IP 与 端口
addr.sin_family = AD_INET; // 知名地址类型是 IPv4
addr.sin_port = htons(8080); // 设置端口
addr.sin_addr.s_addr = INADDR_ANY;
4.绑定端口(服务端)
当调用Socket()接口函数创建了一个套接字之后,必须把套接字与你需要进行通讯的地址建立联系,使用bind函数来进行绑定。
cpp
bind(sock, (sockaddr*)&addr, sizeof(addr));
绑定端口的作用就是告诉操作系统,此端口我占用了,其他端不能抢占我的位置。
如果不进行绑定的话,服务端会不知道监听那个端口,而客户端也无法进行监听。
5.监听(服务端)
当绑定完成之后,服务端必须建立一个监听队列来接受客户端的连接请求。
cpp
int listen(SOCKET s,int backlog)
将socket从普通的状体转化为监听的状态。
这里的参数backlog指的是允许的最大连接队列长度。
6.建立连接
在服务端,使用accept
cpp
SOCKET clientSock = accept(sock, nullptr, nullptr);
从而从连接队列中取出一个已完成三次握手的客户端。
在客户端,使用connect
cpp
connect(sock,(sockaddr*)&serverAddr,sizeof(serverAddr));
从而主动向服务器发起 TCP 三次握手。
此时,TCP已经完成连接建立,可以收发数据。
7.数据收发
此时,在客户端与服务端建立连接以后,这两端都可以进行数据的发送与接收,使用以下方法:
发送:
cpp
int send(SOCKET s,const char FAR* buf,int len,int flags);
接收:
cpp
int recv(SOCKET s,char FAR* buf,int len,int flags);
注意buf这个参数,注意这是将数据存入 TCP 缓冲区,实际是否进行发送或接收由 TCP 决定。
注意flag这个参数,当其值 >0 时,表示接收到数据;当其值 = 0 时,表示对方正常关闭;当其值 < 0 时,表示出现了错误。
8.关闭Socket
当所有收发操作完成之后,我们要关闭Socket,告知对方:我不再通信,从而释放系统资源,防止句柄泄漏。
cpp
closesocket(sock);
9.清理环境
归还 WinSock 初始化时申请的资源,避免资源泄漏与程序异常退出。
cpp
WSACleanuo();
总结一下,Socket的基本使用流程如下:
md
1. WSAStartup // 初始化 WinSock
2. socket // 创建 socket
3. bind // 绑定地址(服务端)
4. listen // 监听(服务端)
5. accept / connect// 建立连接
6. send / recv // 数据通信
7. closesocket // 关闭 socket
8. WSACleanup // 清理 WinSock
Socket的通信模式
1. 阻塞模式(默认)
accept / recv会卡住线程- 适合教学、小工具
- 不适合高并发
2. 非阻塞模式
cpp
u_long mode = 1;
ioctlsocket(sock, FIONBIO, &mode);
- 需要轮询
- CPU 消耗较高
3. 事件驱动(WSAEventSelect)
- 基于事件通知
- 中等规模服务器
4. IOCP(工业级)
Windows 高性能服务器核心技术
- Completion Port
- 异步 + 线程池
- 支撑高并发(如 Web Server、游戏服务器)
- Windows 下高并发服务,基本绕不开 IOCP。
WinSock 与 Linux Socket 的对比
| 对比点 | Windows | Linux |
|---|---|---|
| 初始化 | 必须 WSAStartup |
不需要 |
| 关闭 | closesocket |
close |
| 错误码 | WSAGetLastError() |
errno |
| 高并发 | IOCP | epoll |
| 头文件 | winsock2.h | sys/socket.h |