网络编程-连接、发送、接收数据学习

发布于:2025-09-01 ⋅ 阅读:(13) ⋅ 点赞:(0)

1、概述

连接服务器、向服务器发数据、接受服务器数据,代码你能编写出来么?
 

2、详见代码(vs编译)

main.cpp

#include "NetSocket.h"
#include <string>
#include <iostream>
using namespace std;
int main() {
    // 连接服务器
    const char* ip = "192.168.202.223";
    int port = 8082;
    int connect_timeout = 10;
    bool flag = NetSocket::GetInstance().ConnectServer(ip, port, connect_timeout);
    cout << "connect flag:" << flag << endl;

    // 发送数据
    std::string sendData = "sendData 123456";
    int send_timeout = 10;
    flag = NetSocket::GetInstance().SendData(sendData.c_str(), sendData.length(), send_timeout);
    cout << "send data flag:" << flag << endl;


    // 收数据
    char recvbuf[512] = { 0 };
    // recvBytes实际项目中应该是包头或包体的长度
    int recvBytes = sendData.length();
    int recv_timeout = 10;
    flag = NetSocket::GetInstance().RecvData(recvbuf, recvBytes, recv_timeout);
    cout << "recv data flag:" << flag << endl;
    cout << "recv data:" << recvbuf << endl;

    return 0;
}

NetSocket.h

#ifndef NET_SOCKET_H_
#define NET_SOCKET_H_ 1

class NetSocket
{
private:
    NetSocket();
    ~NetSocket();
    NetSocket(const NetSocket& s) = delete;
    NetSocket& operator=(const NetSocket& s) = delete;

public:
    static NetSocket& GetInstance();

    // 连接服务器
    bool ConnectServer(const char* ip, const int port, int timeout = 10);
    bool SendData(const char* buf, int bufLen, int timeout = 30);
    bool RecvData(char* buf, int bufLen, int timeout = 30);
    void CloseConnect();

private:
    bool InitSocket();
    void UnInitSocket();

    int m_socket;
    bool m_bConnected;
};

#endif

NetSocket.cpp


#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Winsock2.h>
#include "NetSocket.h"
#include <ctime>
#include <string>
#pragma comment(lib, "Ws2_32.lib")

NetSocket::NetSocket() :
    m_socket(INVALID_SOCKET),
    m_bConnected(false)
{
    InitSocket();
}

NetSocket::~NetSocket()
{
    UnInitSocket();
}

NetSocket& NetSocket::GetInstance()
{
    static NetSocket s;
    return s;
}

bool NetSocket::ConnectServer(const char* ip, const int port, int timeout)
{
    m_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (m_socket == INVALID_SOCKET)
    {
        return false;
    }

    // 设置NoDelay
    long noDelay = 1;
    setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, (LPSTR)&noDelay, sizeof(long));

    // 设置非阻塞 1非阻塞 0阻塞
    u_long mode = 1;
    int ret = ::ioctlsocket(m_socket, FIONBIO, &mode);
    if (ret != 0)
    {
        return false;
    }

    struct sockaddr_in addrSrv = { 0 };
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(u_short(port));
    addrSrv.sin_addr.s_addr = inet_addr(ip);
    ret = connect(m_socket, (sockaddr*)&addrSrv, sizeof(addrSrv));
    if (ret == 0)
    {
        m_bConnected = true;
        return true;
    }
    else if (ret == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
    {
        return false;
    }

    // 用io复用函数select,判断sockfd状态
    int maxfd = m_socket + 1;
    fd_set write_fd;
    FD_ZERO(&write_fd);
    FD_SET(m_socket, &write_fd);
    // 1、null:没有事件永久阻塞
    // 2、毫秒、微秒都设置0:扫描下是否有信号,都立刻返回
    // 3、毫秒、微秒大于0:等待指定时间超时3
    struct timeval tv = {timeout, 0};
    int activity = ::select(maxfd, NULL, &write_fd, NULL, &tv);
    if (activity <= 0)
    {
        return false;
    }

    if (FD_ISSET(m_socket, &write_fd))
    {
        m_bConnected = true;
        return true;
    }

    return false;
}

bool NetSocket::SendData(const char* buf, int bufLen, int timeout)
{
    if (!m_bConnected)
    {
        return false;
    }

    int ret = 0;
    int nSendBytes = 0;
    auto start_time = ::time(nullptr);
    while (true)
    {
        ret = send(m_socket, buf + nSendBytes, bufLen - nSendBytes, 0);
        if (ret == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
        {
            // tcp窗口太小发不出去,并且没有超时,继续等待
            if (::time(nullptr) - start_time < timeout)
            {
                // 等待1毫秒,看看内核缓冲区是否有空间
                ::Sleep(1);
                continue;
            }
            else
            {
                CloseConnect();
                return false;
            }
        }
        else if (ret < 1)
        {
            // 出错了, 关闭连接
            CloseConnect();
            return false;
        }

        nSendBytes += ret;
        if (nSendBytes >= bufLen)
        {
            return true;
        }

        // 防止CPU空转
        ::Sleep(1);
    }

    return false;
}

bool NetSocket::RecvData(char* buf, int bufLen, int timeout)
{
    if (!m_bConnected)
    {
        return false;
    }

    int ret = 0;
    int nRecvBytes = 0;
    auto start_time = ::time(nullptr);
    while (true)
    {
        ret = recv(m_socket, buf + nRecvBytes, bufLen - nRecvBytes, 0);
        if (ret == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
        {
            if (::time(nullptr) - start_time < timeout)
            {
                ::Sleep(1);
                continue;
            }
            else
            {
                // 出错了,关闭连接
                CloseConnect();
                return false;
            }
        }
        else if (ret < 1)
        {
            // 对端关闭连接了
            CloseConnect();
            return false;
        }

        nRecvBytes += ret;
        if (nRecvBytes >= bufLen)
        {
            return true;
        }
    }

    return false;
}

void NetSocket::CloseConnect()
{
    if (m_socket == INVALID_SOCKET)
    {
        return;
    }

    closesocket(m_socket);
    m_socket = INVALID_SOCKET;
    m_bConnected = false;
}

bool NetSocket::InitSocket()
{
    WORD wVersionRequested = MAKEWORD(2, 2);
    WSADATA wsaData;
    int nErrorID = ::WSAStartup(wVersionRequested, &wsaData);
    if (nErrorID != 0)
        return FALSE;

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        UnInitSocket();
        return FALSE;
    }

    return TRUE;
}

void NetSocket::UnInitSocket()
{
    CloseConnect();
    ::WSACleanup();
}

学习链接:https://github.com/0voice