Tcp通信的三次握手和四次挥手
TCP的三次握手和四次挥手是TCP连接的建立和断开过程,确保了可靠的数据传输和连接状态的正确管理。
TCP****的三次握手(TCP 链接建立):
- 客户端发送 SYN : 客户端向服务器发送一个 SYN 报文段( SYN=1 , Seq=x ),表示请求建立连接。
- 服务器发送 SYN+ACK : 服务器收到 SYN 报文段后,如果同意建立连接,则会发送一个 SYN+ACK 报文段( SYN=1 , ACK=x+1 , Seq=y ),表示确认客户端的请求,并表示自己也想建立连接。
- 客户端发送 ACK : 客户端收到服务器的 SYN+ACK 报文段后,会发送一个确认报文段( ACK=1 , ACK=y+1 , Seq=x+1 ),表示确认收到服务器的确认,连接建立成功。
TCP****的四次挥手(TCP 链接断开):
- 客户端发送 FIN : 客户端请求断开连接,发送一个 FIN 报文段( FIN=1 , Seq=x )。
- 服务器发送 ACK : 服务器收到客户端的 FIN 报文段后,会发送一个确认报文段( ACK=1 , ACK=x+1 , Seq=y ),表示收到了客户端的请求,但是还有数据需要发送。
- 服务器发送 FIN : 服务器发送完所有数据后,会发送一个 FIN 报文段( FIN=1 , ACK=x+1 , Seq=y ),表示自己也准备断开连接。
- 客户端发送 ACK : 客户端收到服务器的 FIN 报文段后,会发送一个确认报文段( ACK=1 , ACK=y+1 , Seq=x+1 ),表示确认收到服务器的请求,连接断开。
通过三次握手,客户端和服务器之间建立了可靠的双向连接。而通过四次挥手,双方断开了连接,确保数据传输的可靠性和正确性。
客户端:
- 初始化Winsock库: 在使用任何套接字编程功能之前,需要初始化 Winsock 库。可以使用 WSAStartup 函数来完成初始化。
- 创建套接字: 使用 socket 函数创建一个套接字。指定地址族为 AF_INET,类型为 SOCK_STREAM,协议为 IPPROTO_TCP。
- 设置服务器地址和端口: 创建一个 sockaddr_in 结构体,并设置其中的地址族、IP地址和端口号,用于连接服务器。
- 连接服务器: 使用 connect 函数连接到服务器。将创建的套接字和服务器地址作为参数传递给 connect 函数。
- 发送和接收数据: 使用 send 函数向服务器发送数据,并使用 recv 函数接收服务器的响应数据。
- 关闭套接字: 使用 closesocket 函数关闭套接字。
- 清理Winsock库: 在程序退出前,使用 WSACleanup 函数清理 Winsock 库的资源。
cpp
#include <iostream>
#include <string>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main() {
// 初始化Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOK = WSAStartup(ver, &wsData);
if (wsOK != 0) {
std::cerr << "Error: Can't initialize Winsock! Quitting" << std::endl;
return 1;
}
// 创建socket
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "Error: Can't create socket! Quitting" << std::endl;
WSACleanup();
return 1;
}
// 连接到服务器
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(54000);
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
if (connect(clientSocket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
std::cerr << "Error: Can't connect to server! Quitting" << std::endl;
closesocket(clientSocket);
WSACleanup();
return 1;
}
// 发送和接收数据
char buffer[4096];
std::string userInput;
do {
std::cout << "Enter a message: ";
std::getline(std::cin, userInput);
int sendResult = send(clientSocket, userInput.c_str(), userInput.size() + 1, 0);
if (sendResult != SOCKET_ERROR) {
// 接收服务器回复
memset(buffer, 0, sizeof(buffer)); // 清空缓冲区
int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
if (bytesReceived > 0) {
std::cout << "Server response: " << std::string(buffer, bytesReceived) << std::endl;
}
}
else {
std::cerr << "Error: Send failed! Quitting" << std::endl;
break;
}
} while (userInput != "quit");
// 关闭客户端socket
closesocket(clientSocket);
// 关闭Winsock
WSACleanup();
return 0;
}
服务端:
在Windows平台上创建TCP服务器的基本步骤:
- 初始化Winsock库: 在使用任何套接字编程功能之前,需要初始化 Winsock 库。可以使用 WSAStartup 函数来完成初始化。
- 创建套接字: 使用 socket 函数创建一个套接字。指定地址族为 AF_INET ,类型为 SOCK_STREAM ,协议为 IPPROTO_TCP 。
- 设置服务器地址和端口: 创建一个 sockaddr_in 结构体,并设置其中的地址族、IP地址和端口号,用于绑定服务器的地址和端口。
- 绑定地址和端口: 使用 bind 函数将套接字绑定到指定的地址和端口上。
- 监听连接请求: 使用 listen 函数开始监听来自客户端的连接请求。
- 接受连接: 使用 accept 函数接受客户端的连接请求,并创建一个新的套接字用于与客户端通信。在接受连接之前,服务器会阻塞在这一步,直到有客户端连接上来。
- 接收和发送数据: 使用新创建的套接字进行数据的接收和发送。可以使用 recv 函数接收来自客户端的数据,使用 send 函数发送数据给客户端。
- 关闭连接: 当通信完成后,使用 closesocket 函数关闭与客户端的连接。
- 关闭服务器套接字: 当服务器不再接受新的连接时,使用 closesocket 函数关闭服务器套接字。
cpp
#include <iostream>
#include <string>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main() {
// 初始化Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOK = WSAStartup(ver, &wsData);
if (wsOK != 0) {
std::cerr << "Error: Can't initialize Winsock! Quitting" << std::endl;
return 1;
}
// 创建socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket == INVALID_SOCKET) {
std::cerr << "Error: Can't create socket! Quitting" << std::endl;
WSACleanup();
return 1;
}
// 绑定地址和端口
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
hint.sin_addr.S_un.S_addr = INADDR_ANY; // 监听所有网卡上的连接
bind(serverSocket, (sockaddr*)&hint, sizeof(hint));
// 开始监听
listen(serverSocket, SOMAXCONN);
// 接受连接
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&client, &clientSize);
// 关闭服务器socket
closesocket(serverSocket);
// 接收和发送数据
char buffer[4096];
while (true) {
memset(buffer, 0, sizeof(buffer)); // 清空缓冲区
int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
if (bytesReceived == SOCKET_ERROR) {
std::cerr << "Error in recv(). Quitting" << std::endl;
break;
}
if (bytesReceived == 0) {
std::cout << "Client disconnected" << std::endl;
break;
}
std::cout << "Received: " << std::string(buffer, 0, bytesReceived) << std::endl;
// Echo回消息给客户端
send(clientSocket, buffer, bytesReceived, 0);
}
// 关闭客户端socket
closesocket(clientSocket);
// 关闭Winsock
WSACleanup();
return 0;
}