自学内容网 自学内容网

C++ —— 网络通信

        之前在Linux系统下介绍了多种实现网络通信的方式,从本文开始后面的文章将在Windows系统下用C++为大家介绍技术,敬请期待~。

        话不多说,直接进入正文,我们知道,要完成网络通信要用到非常多的函数,并且函数的参数比较复杂,所以小编在想封装一个network的工具方便后面要写通信的时候直接调用。工具包括一个头文件network.h和一个 .cpp 文件network.cpp。头文件中定义一个NetWork类,成员变量包含了通信要用到的诸如描述符和通信地址的信息;成员函数就是通信过程中用到的函数。

        network.h头文件如下:

#ifndef NETWORK_H
#define NETWORK_H

#include <iostream>
#include <sys/types.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

class NetWork
{
    int sock;           //socket对象描述符
    int type;           //协议类型
    sockaddr_in addr;   //通信地址
    socklen_t addrlen;  //地址结构字节数
    bool is_svr;        //是否为服务器端
public:
    NetWork(void);
    NetWork(int type,const char *ip,short port,bool is_svr=false);
    ~NetWork(void);
    bool open(void);
    NetWork* accept(void);
    int send(const char *buf,int flag=0);
    int send(const void *buf,size_t len,int flag=0);
    int recv(void *buf,size_t len,int flag=0);
};


#endif // NETWORK_H

下面要在network.cpp文件中去一一定义NetWork的成员函数,也是工具的主要逻辑所在。为了通用性考虑,我们在NetWork类中专门定义了协议类型type去区分TCP通信和UDP通信协议以及定义了is_svr去区分调用服务器端和客户端,并在函数定义中去判断从而不影响各自的网络通信步骤。

#include "network.h"
using namespace std;

NetWork::NetWork(void)
{
    addrlen = sizeof(addr);
    type = SOCK_STREAM;
    is_svr = false;
}

NetWork::NetWork(int type, const char *ip, short port, bool is_svr):type(type), is_svr(is_svr)
{
    // 不在构造函数中创建socket,因为socket创建可能失败,而构造函数是没有返回值的,不能在此创建
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip);
    addrlen = sizeof(addr);
}

NetWork::~NetWork(void)
{
    close(sock);
}

bool NetWork::open(void)
{
    WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
perror("WSADATA失败");
}
    //创建socket对象
    sock = socket(AF_INET, type, 0);
    if (sock < 0)
    {
        perror("socket");
        return false;
    }
    //根据type和is_svr执行以下流程
    if(is_svr)
    {
        if(bind(sock, (sockaddr*)&addr, addrlen))
        {
            perror("bind");
            return false;
        }
        if(SOCK_STREAM == type && listen(sock, 50))
        {
            perror("listen");
            return false;
        }
    }
    else if(SOCK_STREAM == type && connect(sock, (sockaddr*)&addr, addrlen))
    {
        perror("connect");
        return false;
    }
    return true;
}

NetWork *NetWork::accept(void) 
{
    if(SOCK_STREAM!= type || !is_svr)
    {
        puts("只有type为SOCK_STREAM且为服务端才能调用该函数\n");
        return NULL;
    }

    NetWork *nw = new NetWork;
    nw->sock = ::accept(sock, (sockaddr*)&nw->addr, &nw->addrlen);
    if(nw->sock < 0)
    {
        perror("accept");
        delete nw;
        return NULL;
    }
    return nw;
}

int NetWork::send(const char *buf, int flag) 
{
    if(SOCK_STREAM == type)
         return ::send(sock, buf, strlen(buf)+1, flag);
    else
         return sendto(sock, buf, strlen(buf)+1, flag, (sockaddr*)&addr, addrlen);
}

int NetWork::send(const void *buf, size_t len, int flag) 
{
    if(SOCK_STREAM == type)
        return ::send(sock, (const char*)buf, len, flag);
    else
        return sendto(sock, (const char*)buf, len, flag, (sockaddr*)&addr, addrlen);
}

int NetWork::recv(void *buf, size_t len, int flag) 
{
    if(SOCK_STREAM == type)
        return ::recv(sock, (char*)buf, len, flag);
    else
        return recvfrom(sock, (char*)buf, len, flag, (sockaddr*)&addr, &addrlen);
}

为了测试工具是否具备以上介绍的功能,我们准备一个TCP客户端和一个服务器进行通信以进行验证:

服务端:

#include "network.h"
using namespace std;

#define BUF_SIZE (4096)

void *run(void *arg)
{
NetWork *cnw = static_cast<NetWork *>(arg);
char *buf = new char[BUF_SIZE];

while (1)
{
int ret = cnw->recv(buf, BUF_SIZE);
if (ret <= 0 || 0 == strcmp(buf, "quit"))
{
puts("客户端退出!\n");
delete cnw;
delete buf;
return NULL;
}

printf("recv:%s bits:%d\n", buf, ret);
strcat(buf, ":return");

ret = cnw->send(buf);
if (ret <= 0)
{
puts("客户端退出!\n");
delete cnw;
delete buf;
return NULL;
}
}
}

int main(int argc, const char *argv[])
{
if (3 != argc)
{
printf("User: server <ip> <port>");
return 0;
}

// WSADATA wsaData;
// if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
// {
// perror("WSADATA失败");
// }

NetWork *snw = new NetWork(SOCK_STREAM, argv[1], atoi(argv[2]), true);
if (!snw->open())
{
delete snw;
return -1;
}
while (1)
{
NetWork *cnw = snw->accept();
if (NULL == cnw)
{
continue;
}

pthread_t tid;
pthread_create(&tid, NULL, run, (void *)cnw);
}

return 0;
}

客户端:

#include "network.h"
using namespace std;

int main(int argc, const char *argv[])
{
    // WSADATA wsaData;
    // if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    // {
    //     perror("WSADATA失败");
    // }

    NetWork *cnw = new NetWork(SOCK_STREAM, argv[1], atoi(argv[2]), false);
    if (!cnw->open())
    {
        delete cnw;
        return -1;
    }
    char buf[4096];
    size_t buf_size = sizeof(buf);

    while (1)
    {
        cout << ">>>>>" << endl;
        cin.getline(buf, buf_size);
        int ret = cnw->send(buf, strlen(buf) + 1, 0);
        if (ret <= 0 || 0 == strcmp("quit", buf))
        {
            printf("通信结束!\n");
            delete cnw;
            break;
        }
        ret = cnw->recv(buf, buf_size, 0);
        if (ret <= 0)
        {
            delete cnw;
            break;
        }
        printf("recv:%s bits:%d\n", buf, ret);
    }

    return 0;
}

下面我们先启动服务器再运行客户端进行测试,测试结果如下:

上图中,左边是服务器端,右边是客户端,可以看到,由客户端发送消息给服务端,服务端可以正常接收消息并响应客户端返回,客户端也可以接收服务器的响应,由此完成通信。

over


原文地址:https://blog.csdn.net/helloworld1932/article/details/143477229

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!