基于windowns下的TCP网络通信编程

发布于:2024-04-19 ⋅ 阅读:(24) ⋅ 点赞:(0)

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;
}