Tcp通信的三次握手和四次挥手
TCP的三次握手和四次挥手是TCP连接的建立和断开过程,确保了可靠的数据传输和连接状态的正确管理。
TCP的三次握手(TCP 链接建立):
1.
客户端发送
SYN
:
客户端向服务器发送一个
SYN
报文段(
SYN=1
,
Seq=x
),表示请求建立连接。
2.
服务器发送
SYN+ACK
:
服务器收到
SYN
报文段后,如果同意建立连接,则会发送一个
SYN+ACK
报文段(
SYN=1
,
ACK=x+1
,
Seq=y
),表示确认客户端的请求,并表示自己也想建立连接。
3.
客户端发送
ACK
:
客户端收到服务器的
SYN+ACK
报文段后,会发送一个确认报文段(
ACK=1
,
ACK=y+1
,
Seq=x+1
),表示确认收到服务器的确认,连接建立成功。
TCP的四次挥手(TCP 链接断开):
1.
客户端发送
FIN
:
客户端请求断开连接,发送一个
FIN
报文段(
FIN=1
,
Seq=x
)。
2.
服务器发送
ACK
:
服务器收到客户端的
FIN
报文段后,会发送一个确认报文段(
ACK=1
,
ACK=x+1
,
Seq=y
),表示收到了客户端的请求,但是还有数据需要发送。
3.
服务器发送
FIN
:
服务器发送完所有数据后,会发送一个
FIN
报文段(
FIN=1
,
ACK=x+1
,
Seq=y
),表示自己也准备断开连接。
4.
客户端发送
ACK
:
客户端收到服务器的
FIN
报文段后,会发送一个确认报文段(
ACK=1
,
ACK=y+1
,
Seq=x+1
),表示确认收到服务器的请求,连接断开。
通过三次握手,客户端和服务器之间建立了可靠的双向连接。而通过四次挥手,双方断开了连接,确保数据传输的可靠性和正确性。
客户端:
1.
初始化Winsock库:
在使用任何套接字编程功能之前,需要初始化 Winsock 库。可以使用
WSAStartup
函数来完成初始化。
2.
创建套接字:
使用
socket
函数创建一个套接字。指定地址族为 AF_INET,类型为 SOCK_STREAM,协议为 IPPROTO_TCP。
3.
设置服务器地址和端口:
创建一个 sockaddr_in 结构体,并设置其中的地址族、IP地址和端口号,用于连接服务器。
4.
连接服务器:
使用
connect
函数连接到服务器。将创建的套接字和服务器地址作为参数传递给
connect
函数。
5.
发送和接收数据:
使用
send
函数向服务器发送数据,并使用
recv
函数接收服务器的响应数据。
6.
关闭套接字:
使用
closesocket
函数关闭套接字。
7.
清理Winsock库:
在程序退出前,使用
WSACleanup
函数清理 Winsock 库的资源。
#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服务器的基本步骤:
1.
初始化Winsock库:
在使用任何套接字编程功能之前,需要初始化 Winsock 库。可以使用
WSAStartup
函数来完成初始化。
2.
创建套接字:
使用
socket
函数创建一个套接字。指定地址族为
AF_INET
,类型为
SOCK_STREAM
,协议为
IPPROTO_TCP
。
3.
设置服务器地址和端口:
创建一个
sockaddr_in
结构体,并设置其中的地址族、IP地址和端口号,用于绑定服务器的地址和端口。
4.
绑定地址和端口:
使用
bind
函数将套接字绑定到指定的地址和端口上。
5.
监听连接请求:
使用
listen
函数开始监听来自客户端的连接请求。
6.
接受连接:
使用
accept
函数接受客户端的连接请求,并创建一个新的套接字用于与客户端通信。在接受连接之前,服务器会阻塞在这一步,直到有客户端连接上来。
7.
接收和发送数据:
使用新创建的套接字进行数据的接收和发送。可以使用
recv
函数接收来自客户端的数据,使用
send
函数发送数据给客户端。
8.
关闭连接:
当通信完成后,使用
closesocket
函数关闭与客户端的连接。
9.
关闭服务器套接字:
当服务器不再接受新的连接时,使用
closesocket
函数关闭服务器套接字。
#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;
}