一个基于TCP/UDP的网络通信代码类分享

        笔者在C++的学习过程中封装的一个简单的基于TCP 和 UDP 的网络通信类 Network,具有创建服务器和客户端的基本功能。

1、主要功能

  1. Socket 创建与初始化

    • 支持创建 TCP (SOCK_STREAM) 和 UDP (SOCK_DGRAM) 协议的 socket。
    • 通过构造函数初始化 socket,包括设置 IP 地址和端口号。
  2. 服务端功能

    • 支持 TCP 服务端绑定本地地址和端口,并监听连接请求。
    • 能够接受客户端连接并返回一个新的 Network 对象,代表新的客户端会话。
  3. 客户端功能

    • 支持通过 TCP 连接到指定的服务端 IP 和端口。
  4. 数据发送与接收

    • 提供简便的方法发送和接收数据,无论是使用 UDP 还是 TCP。
    • 支持发送反馈消息到客户端。
  5. 资源管理

    • 在析构函数中自动关闭 socket,确保资源及时释放。
  6. 获取客户端 IP

    • 通过 get_ip_nw 方法获取连接的客户端 IP 地址。

2、使用场景

         该类可以用于实现基本的网络服务,例如聊天应用、数据传输工具等,是学习网络编程的良好示范。

3、代码结构

  • Network::Network 构造函数:初始化网络,设置 socket。

  • Network::~Network 析构函数:关闭网络连接。

  • accept_nw:接受客户端连接。

  • send_nw 和 recv_nw:处理数据的发送与接收。

  • send_feedback_nw:发送反馈信息。

  • close_nw:关闭 socket 确保资源释放。

  • get_ip_nw:获取客户端的 IP 地址信息。

4、 代码

头文件:

#include <iostream>
#include <cstring>
#include <winsock2.h>
#include <cstdio>
#include <cstdlib>
#include <ws2tcpip.h>

class Network
{
    private:
        int type;//通信协议类型
        int sockfd;//套接字描述符
        struct sockaddr_in addr;//通信地址
        socklen_t addrlen;//通信地址长度
        bool is_svr;//是否是服务器端
    public:
        Network(int type,short port,const char* ip,bool is_svr);//构造函数,初始化套接字等信息
        
        ~Network();

        Network* accept_nw();//只有tcp服务器端才有用,用于接受客户端连接
        int send_nw(void* buf,size_t len);
        int recv_nw(void* buf,size_t len);
        int send_feedback_nw(void* feedback);
        void close_nw();
        const char* get_ip_nw();
};

成员函数:

#include "network.hpp"

Network::Network(int type, short port, const char *ip, bool is_svr) : type(type), is_svr(is_svr), sockfd(-1), addrlen(sizeof(sockaddr_in))
{
    sockfd = socket(AF_INET, type, 0);
    if (sockfd < 0)
    {
        perror("Network::Network: socket");
        return;
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip);

    if (is_svr)
    {
        if (type == SOCK_STREAM) // 是TCP服务端
        {
            if (bind(sockfd, (sockaddr *)&addr, addrlen))
            {
                perror("Network::Network: bind");
                closesocket(sockfd);
                return;
            }
            if (listen(sockfd, 10) < 0)
            {
                perror("Network::Network: listen");
                closesocket(sockfd);
                return;
            }
        }
        else if (type == SOCK_DGRAM)
        {
            if (bind(sockfd, (sockaddr *)&addr, addrlen))
            {
                perror("Network::Network: bind");
                closesocket(sockfd);
                return;
            }
        }
    }
    else
    {
        if (type == SOCK_STREAM)
        {
            if (connect(sockfd, (sockaddr *)&addr, addrlen))
            {
                perror("Network::Network: connect");
                closesocket(sockfd);
                return;
            }
        }
    }
}

Network::~Network()
{
    close_nw();
}

Network *Network::accept_nw()
{
    if (SOCK_STREAM != type || !is_svr)
    {
        perror("Network::accept_nw: not a server socket");
        return NULL;
    }
    Network *nw = new Network(SOCK_STREAM, 0, nullptr, false); // 创建新的网络对象
    if (!nw)
    {
        perror("Network::accept_nw: new Network");
        return NULL;
    }
    memset(&nw->addr, 0, sizeof(nw->addr));
    nw->addrlen = sizeof(sockaddr_in);

    nw->sockfd = accept(sockfd, (sockaddr *)&nw->addr, &nw->addrlen); // 接收连接请求
    if (nw->sockfd < 0)
    {
        perror("Network::accept_nw: accept");
        delete nw;
        return NULL;
    }
    return nw;
}

int Network::send_nw(void *buf, size_t len)
{
    if (type == SOCK_DGRAM)
    {
        return sendto(sockfd, (const char *)buf, len, 0, (sockaddr *)&addr, addrlen);
    }
    else
    {
        return send(sockfd, (const char *)buf, len, 0);
    }
}

int Network::recv_nw(void *buf, size_t len)
{
    if (type == SOCK_DGRAM)
    {
        return recvfrom(sockfd, (char *)buf, len, 0, (sockaddr *)&addr, &addrlen);
    }
    else
    {
        return recv(sockfd, (char *)buf, len, 0);
    }
}

int Network::send_feedback_nw(void *feedback)
{
    return send_nw(feedback, strlen((const char *)feedback));
}

void Network::close_nw()
{
    closesocket(sockfd);
}

const char *Network::get_ip_nw()
{
    return inet_ntoa(addr.sin_addr);
}

5、简单的服务端使用示例

#include "network.hpp"

int main(int argc, const char *argv[])
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        std::cerr << "WSAStartup failed: " << WSAGetLastError() << std::endl;
        return 0;
    }

    if (argc < 3)
    {
        std::cerr << "使用方法: " << argv[0] << " <端口号> <IP地址>" << std::endl;
        return 1;
    }

    // 初始化服务端
    Network *server = new Network(SOCK_STREAM, atoi(argv[1]), argv[2], true);
    if (!server)
    {
        std::cerr << "无法初始化服务端" << std::endl;
        return 1;
    }
    std::cout << "server端在端口 " << argv[1] << " 打开成功" << std::endl;

    // 等待客户端连接
    Network *client = server->accept_nw();
    if (!client)
    {
        std::cerr << "无法等待连接client端" << std::endl;
        server->close_nw();
        delete server;
        return 1;
    }
    std::cout << "client端连接:" << client->get_ip_nw() << std::endl;

    char buffer[1024];
    for (;;)
    {
        // 接收数据
        int bytes_received = client->recv_nw(buffer, sizeof(buffer) - 1); // 留一个字节给'\0'
        if (bytes_received < 0)
        {
            std::cerr << "接收数据失败" << std::endl;
            break;
        }
        if (bytes_received == 0 || strcmp("quit", buffer) == 0)
        {
            std::cout << "client端:" << client->get_ip_nw() << " 取消连接" << std::endl;
            break;
        }

        buffer[bytes_received] = '\0'; // 确保以'\0'结尾
        std::cout << "接收到: " << buffer << std::endl;

        // 发送反馈
        char feedback[1024];
        snprintf(feedback, sizeof(feedback), "return:%s", buffer); // 创建反馈信息
        int bytes_sent = client->send_feedback_nw(feedback);
        if (bytes_sent > 0)
        {
            std::cout << "Sent feedback: " << feedback << std::endl;
        }
    }
    client->close_nw(); // 关闭客户端连接
    server->close_nw(); // 关闭服务器连接
    delete client;      // 释放客户端对象
    delete server;      // 释放服务器对象
    return 0;
}

6、总结

        此 Network 类提供了一个简单清晰的接口来处理网络通信,让开发者可以轻松地建立服务器与客户端之间的联系,以及进行数据传输。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值