【Linux加餐-验证UDP:TCP】-windows作为client访问Linux

发布于:2025-03-28 ⋅ 阅读:(33) ⋅ 点赞:(0)

一、验证UDP-windows作为client访问Linux

UDP client样例代码

#include <iostream>
#include <cstdio>
#include <thread>
#include <string>
#include <cstdlib>
#include <WinSock2.h>
#include <Windows.h>

#pragma warning(disable : 4996)

#pragma comment(lib, "ws2_32.lib")

std::string serverip = "";  // 填写你的云服务器ip
uint16_t serverport = 8888; // 填写你的云服务开放的端口号

int main()
{
    WSADATA wsd;
    WSAStartup(MAKEWORD(2, 2), &wsd);

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport); //?
    server.sin_addr.s_addr = inet_addr(serverip.c_str());

    SOCKET sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == SOCKET_ERROR)
    {
        std::cout << "socker error" << std::endl;
        return 1;
    }
    std::string message;
    char buffer[1024];
    while (true)
    {
        std::cout << "Please Enter@ ";
        std::getline(std::cin, message);
        if(message.empty()) continue;
        sendto(sockfd, message.c_str(), (int)message.size(), 0, (struct sockaddr *)&server, sizeof(server));
        struct sockaddr_in temp;
        int len = sizeof(temp);
        int s = recvfrom(sockfd, buffer, 1023, 0, (struct sockaddr *)&temp, &len);
        if (s > 0)
        {
            buffer[s] = 0;
            std::cout << buffer << std::endl;
        }
    }

    closesocket(sockfd);
    WSACleanup();
    return 0;
}

UdpServer

#include <iostream>
#include <string>
#include <memory>
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

const static uint16_t defaultport = 8888;
const static int defaultfd = -1;
const static int defaultsize = 1024;

enum
{
    Usage_Err = 1,
    Socket_Err,
    Bind_Err
};

class UdpServer
{
public:
    UdpServer(uint16_t port = defaultport)
        : _port(port), _sockfd(defaultfd)
    {
    }
    void Init()
    {
        // 1. 创建socket,就是创建了文件细节
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            exit(Socket_Err);
        }

        // 2. 绑定,指定网络信息
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // memset
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY; // 1. 4字节IP 2. 变成网络序列

        // 结构体填完,设置到内核中了吗??没有
        int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
        if (n != 0)
        {
            exit(Bind_Err);
        }
    }
    void Start()
    {
        // 服务器永远不退出
        char buffer[defaultsize];
        for (;;)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer); // 不能乱写
            ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (n > 0)
            {
                uint16_t clientport = ntohs(peer.sin_port);
                std::string clientip = inet_ntoa(peer.sin_addr);
                std::string prefix = clientip + ":" + std::to_string(clientport);
                buffer[n] = 0;
                std::cout << prefix << "# " << buffer << std::endl;


                std::string echo = buffer;
                echo += "[udp server echo message]";
                sendto(_sockfd, echo.c_str(), echo.size(), 0, (struct sockaddr *)&peer, len);
            }
        }
    }
    ~UdpServer()
    {
    }

private:
    uint16_t _port;
    int _sockfd;
};

void Usage(std::string proc)
{
    std::cout << "Usage : \n\t" << proc << " local_port\n"
              << std::endl;
}

// ./udp_server 8888
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return Usage_Err;
    }

    uint16_t port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port);
    usvr->Init();
    usvr->Start();

    return 0;
}

测试结果

在这里插入图片描述

二、验证TCP-windows作为client访问Linux

TcpClient.cc

#include <winsock2.h>
#include <iostream>
#include <string>

#pragma warning(disable : 4996)

#pragma comment(lib, "ws2_32.lib")

std::string serverip = "";  // 填写你的云服务器ip
uint16_t serverport = 8888; // 填写你的云服务开放的端口号

int main()
{
    WSADATA wsaData;
    int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (result != 0)
    {
        std::cerr << "WSAStartup failed: " << result << std::endl;
        return 1;
    }

    SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (clientSocket == INVALID_SOCKET)
    {
        std::cerr << "socket failed" << std::endl;
        WSACleanup();
        return 1;
    }

    sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(serverport);                  // 替换为服务器端口
    serverAddr.sin_addr.s_addr = inet_addr(serverip.c_str()); // 替换为服务器IP地址

    result = connect(clientSocket, (SOCKADDR *)&serverAddr, sizeof(serverAddr));
    if (result == SOCKET_ERROR)
    {
        std::cerr << "connect failed" << std::endl;
        closesocket(clientSocket);
        WSACleanup();
        return 1;
    }
    while (true)
    {
        std::string message;
        std::cout << "Please Enter@ ";
        std::getline(std::cin, message);
        if(message.empty()) continue;
        send(clientSocket, message.c_str(), message.size(), 0);

        char buffer[1024] = {0};
        int bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);
        if (bytesReceived > 0)
        {
            buffer[bytesReceived] = '\0'; // 确保字符串以 null 结尾
            std::cout << "Received from server: " << buffer << std::endl;
        }
        else
        {
            std::cerr << "recv failed" << std::endl;
        }
    }

    closesocket(clientSocket);
    WSACleanup();

    return 0;
}

TcpServer.cc

#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>

const static int default_backlog = 6;

enum
{
    Usage_Err = 1,
    Socket_Err,
    Bind_Err,
    Listen_Err
};

#define CONV(addr_ptr) ((struct sockaddr *)addr_ptr)

class TcpServer
{
public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
    }
    // 都是固定套路
    void Init()
    {
        // 1. 创建socket, file fd, 本质是文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            exit(0);
        }
        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        // 2. 填充本地网络信息并bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);

        // 2.1 bind
        if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
        {
            exit(Bind_Err);
        }

        // 3. 设置socket为监听状态,tcp特有的
        if (listen(_listensock, default_backlog) != 0)
        {
            exit(Listen_Err);
        }
    }
    void ProcessConnection(int sockfd, struct sockaddr_in &peer)
    {
        uint16_t clientport = ntohs(peer.sin_port);
        std::string clientip = inet_ntoa(peer.sin_addr);
        std::string prefix = clientip + ":" + std::to_string(clientport);
        std::cout << "get a new connection, info is : " << prefix << std::endl;
        while (true)
        {
            char inbuffer[1024];
            ssize_t s = ::read(sockfd, inbuffer, sizeof(inbuffer)-1);
            if(s > 0)
            {
                inbuffer[s] = 0;
                std::cout << prefix << "# " << inbuffer << std::endl;
                std::string echo = inbuffer;
                echo += "[tcp server echo message]";
                write(sockfd, echo.c_str(), echo.size());
            }
            else
            {
                std::cout << prefix << " client quit" << std::endl;
                break;
            }
        }
    }
    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, CONV(&peer), &len);
            if (sockfd < 0)
            {
                continue;
            }
            ProcessConnection(sockfd, peer);
        }
    }
    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock; // TODO
    bool _isrunning;
};

using namespace std;

void Usage(std::string proc)
{
    std::cout << "Usage : \n\t" << proc << " local_port\n"
              << std::endl;
}
// ./tcp_server 8888
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return Usage_Err;
    }
    uint16_t port = stoi(argv[1]);
    std::unique_ptr<TcpServer> tsvr = make_unique<TcpServer>(port);
    tsvr->Init();
    tsvr->Start();

    return 0;
}

测试结果

在这里插入图片描述

三、注意

• 一定要开放云服务器对应的端口号,在你的阿里云或者腾讯云或者华为云的网站后台中开放。

• 我们发现可以 udp tcpclient(Windows)和 tcpserver(Linux)可以通信

WinSock2.h 是 Windows Sockets API(应用程序接口)的头文件,用于在 Windows 平台上进行网络编程。它包含了 Windows Sockets 2(winsock2)所需的数据类型、函数声明和结构定义,使得开发者能够创建和使用套接字(sockets)进行网络通信。

在编写使用 Winsock2 的程序时,需要在源文件中包含 WinSock2.h 头文件。这样,编译器就能够识别并理解 winsock2 中定义的数据类型和函数,从而能够正确地编译和链接网络相关的代码。

此外,与 WinSock2.h 头文件相对应的是 ws2_32.lib 库文件。在链接阶段,需要将这个库文件链接到程序中,以确保运行时能够找到并调用 Winsock2 API 中实现的函数。

在 WinSock2.h 中定义了一些重要的数据类型和函数,如:
WSADATA:保存初始化 Winsock 库时返回的信息。
SOCKET:表示一个套接字描述符,用于在网络中唯一标识一个套接字。
sockaddr_in:IPv4 地址结构体,用于存储 IP 地址和端口号等信息。
socket ():创建一个新的套接字。
bind ():将套接字与本地地址绑定。
listen ():将套接字设置为监听模式,等待客户端的连接请求。
accept ():接受客户端的连接请求,并返回一个新的套接字描述符,用于与客户端进行通信。

WSAStartup 函数是 Windows Sockets API 的初始化函数,它用于初始化 Winsock 库。该函数在应用程序或 DLL 调用任何 Windows 套接字函数之前必须首先执行,它扮演着初始化的角色。

以下是 WSAStartup 函数的一些关键点:
它接受两个参数:wVersionRequested 和 lpWSAData。wVersionRequested 用于指定所请求的 Winsock 版本,通常使用 MAKEWORD (major, minor) 宏,其中 major 和 minor 分别表示请求的主版本号和次版本号。lpWSAData 是一个指向 WSADATA 结构的指针,用于接收初始化信息。

如果函数调用成功,它会返回 0;否则,返回错误代码。

WSAStartup 函数的主要作用是向操作系统说明我们将使用哪个版本的 winsock 库,从而使得该库文件能与当前的操作系统协同工作。成功调用该函数后,Winsock 库的状态会被初始化,应用程序就可以使用 Winsock 提供的一系列套接字服务,如地址家族识别、地址转换、名字查询和连接控制等。这些服务使得应用程序可以与底层的网络协议栈进行交互,实现网络通信。

在调用 WSAStartup 函数后,如果应用程序完成了对请求的 Socket 库的使用,应调用 WSACleanup 函数来解除与 Socket 库的绑定并释放所占用的系统资源。