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

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

UDP client样例代码

cc 复制代码
#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

cc 复制代码
#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

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

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 库的绑定并释放所占用的系统资源。

相关推荐
Fireworkitte17 分钟前
Ubuntu、CentOS、AlmaLinux 9.5的 rc.local实现 开机启动
linux·ubuntu·centos
sword devil9001 小时前
ubuntu常见问题汇总
linux·ubuntu
ac.char1 小时前
在CentOS系统中查询已删除但仍占用磁盘空间的文件
linux·运维·centos
淮北也生橘123 小时前
Linux的ALSA音频框架学习笔记
linux·笔记·学习
iナナ4 小时前
传输层协议——UDP和TCP
网络·网络协议·tcp/ip·udp
华强笔记6 小时前
Linux内存管理系统性总结
linux·运维·网络
十五年专注C++开发6 小时前
CMake进阶: CMake Modules---简化CMake配置的利器
linux·c++·windows·cmake·自动化构建
phoenix09817 小时前
ansible部署lnmp-allinone
linux·运维·ansible
winds~7 小时前
【git】 撤销revert一次commit中的某几个文件
linux·c++
iY_n8 小时前
Linux网络基础
linux·网络·arm开发