socket編程學(xué)習(xí)代碼樣例

學(xué)習(xí)編程最好的方式是寫代碼實(shí)踐窑多。

學(xué)習(xí)樣例

網(wǎng)絡(luò)編程常用的函數(shù):

  • send(); recv();
  • socket(); bind(); listen(); accept(); connect();
  • getsockopt(); setsockopt();
  • closesocket()close()揭保,Windows上有些不同
  • getaddrinfo(); freeaddrinfo();
  • getsockname(); inet_ntop(); inet_pton();

例子在windows vs和mac g++上測(cè)試過(guò)柱恤。
mac上編譯命令:g++ --std=c++11 main.cpp奴紧。

#include<algorithm>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<stdint.h>
#include<assert.h>
#include <fcntl.h>
#ifdef WIN32
#include <ws2tcpip.h>
#include <mstcpip.h>
//#include <winsock.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib") // 不然鏈接出錯(cuò)
#include <windows.h>
#else
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif

// 輸出log
namespace {
    class  LogAddEnd
    {
    public:
        void operator=(std::ostream& other)
        {
            other << std::endl;
        }
    };
#define LOG LogAddEnd() = std::cout << __LINE__ << " " << "["<<__FUNCTION__<<"] "
#define LOG_NEW_LINE std::cout << std::endl
}

static int LastErrorNo()
{
#if WIN32
    return WSAGetLastError();
#else
    return errno;
#endif
}

#if WIN32
static std::string LastErrorString()
{
    LPSTR pszError = NULL;

    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_ENGLISH_US), (LPSTR)&pszError, 0, NULL);

    std::string&& szErrorString = std::string(pszError);

    LocalFree(pszError);

    return szErrorString;
}

#else
static std::string LastErrorString()
{
    return std::string(strerror(LastErrorNo()));
}
#endif

#define LOG_NET_ERR_INFO LOG << "net_errno:" << LastErrorNo() << " msg:" << LastErrorString()

namespace NetUtils {

    void SleepSomeSeconds(int seconds)
    {
#ifdef WIN32
        Sleep(seconds * 1000);
#else
        sleep(seconds);
#endif // WIN32
    }

    // 在mac上沒(méi)有效果哎,win32下有效
    std::string GetLocalIPAddress()
    {
        char ip[128] = { 0 };
        addrinfo hints;
        addrinfo *answer = nullptr,*curr = nullptr;
        
        memset(&hints, 0, sizeof(addrinfo));
        hints.ai_family = AF_INET; /* Allow IPv4 */
        hints.ai_protocol = 0; /* Any protocol */
        hints.ai_socktype = SOCK_STREAM;

        // mac上第二個(gè)參數(shù)傳nullptr侥祭,報(bào)錯(cuò)宵呛。
        int ret = getaddrinfo("", "80", &hints, &answer);
        if (ret == 0)
        {
            for (curr = answer; curr != NULL; curr = curr->ai_next)
            {
                if (curr->ai_family == AF_INET)
                {
                    sockaddr_in *addr = reinterpret_cast<sockaddr_in *>(curr->ai_addr);
                    inet_ntop(curr->ai_family, &addr->sin_addr, ip, sizeof(ip));
                    if (strcmp(ip, "127.0.0.1") == 0)
                    {
                        continue;
                    }
                }
                else if (curr->ai_family == AF_INET6)
                {
                    sockaddr_in6 *addr = reinterpret_cast<sockaddr_in6 *>(curr->ai_addr);
                    inet_ntop(curr->ai_family, &addr->sin6_addr, ip, sizeof(ip));
                    if (strcmp(ip, "::1") == 0)
                    {
                        continue;
                    }
                }
                else
                {
                    LOG << "check it";
                    continue;
                }
                break;
            }
        }
        freeaddrinfo(answer);
        return ip;
    }

    std::string GetLocalIP(int socket_id)
    {
        sockaddr_storage local_addr;
        socklen_t len = sizeof(local_addr);

        if (getsockname(socket_id, (sockaddr*)&local_addr, &len) != 0)
        {
            LOG_NET_ERR_INFO;
            return "";
        }

        void* ip_ptr = nullptr;
        if (local_addr.ss_family == AF_INET)
        {
            ip_ptr = &((sockaddr_in *)&local_addr)->sin_addr;
        }
        else if (local_addr.ss_family == AF_INET6)
        {
            ip_ptr = &((sockaddr_in6 *)&local_addr)->sin6_addr;
        }
        else
        {
            return "";
        }

        char ip_str[128] = { 0 };
        auto* ptr = inet_ntop(local_addr.ss_family, ip_ptr, ip_str, sizeof(ip_str));
        if (ptr) {
            return ptr;
        }
        else
        {
            LOG_NET_ERR_INFO;
            return "";
        }
    }

    int GetLocalPort(int socket_id)
    {
        sockaddr_storage local_addr;
        socklen_t len;
        len = sizeof(local_addr);
        if (getsockname(socket_id, (sockaddr*)&local_addr, &len) != 0)
        {
            LOG_NET_ERR_INFO;
            return -1;
        }

        if (local_addr.ss_family == AF_INET)
        {
            sockaddr_in *addr4 = (sockaddr_in *)&local_addr;
            return ntohs(addr4->sin_port);
        }
        else if (local_addr.ss_family == AF_INET6)
        {
            sockaddr_in6 *addr6 = (sockaddr_in6 *)&local_addr;
            return  ntohs(addr6->sin6_port);
        }
        else
        {
            return -1;
        }
    }

    std::string GetPeerIP(int socket_id)
    {
        sockaddr_storage local_addr;
        socklen_t len = sizeof(local_addr);

        if (getpeername(socket_id, (sockaddr*)&local_addr, &len) != 0)
        {
            LOG << LastErrorNo() << LastErrorString();
            return "error: getsockname error";
        }

        void* ip_ptr = nullptr;
        if (local_addr.ss_family == AF_INET)
        {
            ip_ptr = &((sockaddr_in *)&local_addr)->sin_addr;
        }
        else if (local_addr.ss_family == AF_INET6)
        {
            ip_ptr = &((sockaddr_in6 *)&local_addr)->sin6_addr;
        }
        else
        {
            return "";
        }

        char ip_str[128] = { 0 };
        auto* ptr = inet_ntop(local_addr.ss_family, ip_ptr, ip_str, sizeof(ip_str));
        if (ptr) {
            return ptr;
        }
        else
        {
            LOG_NET_ERR_INFO;
            return "";
        }
    }

    int GetPeerPort(int socket_id)
    {
        sockaddr_storage local_addr;
        socklen_t len;
        len = sizeof(local_addr);
        if (getpeername(socket_id, (sockaddr*)&local_addr, &len) != 0)
        {
            LOG_NET_ERR_INFO;
            return -1;
        }

        if (local_addr.ss_family == AF_INET)
        {
            sockaddr_in *addr4 = (sockaddr_in *)&local_addr;
            return ntohs(addr4->sin_port);
        }
        else if (local_addr.ss_family == AF_INET6)
        {
            sockaddr_in6 *addr6 = (sockaddr_in6 *)&local_addr;
            return  ntohs(addr6->sin6_port);
        }
        else
        {
            return -1;
        }
    }

    static void CloseSocket(int socket_id)
    {
        if (socket_id != -1)
        {
#if WIN32
            closesocket(socket_id);
#else
            close(socket_id);
#endif
        }
    }

    static void SetKeepAlive(int socket_id, int idle_time, int interval_time)
    {
#if _WIN32
        struct StTcpKeepalive
        {
            uint32_t onoff;
            uint32_t keepalivetime;
            uint32_t keepaliveinterval;
        };
        StTcpKeepalive options;
        options.onoff = 1;
        options.keepalivetime = idle_time * 1000;
        options.keepaliveinterval = interval_time * 1000;
        DWORD cbBytesReturned;
        WSAIoctl(socket_id, SIO_KEEPALIVE_VALS, &options, sizeof(options), NULL, 0, &cbBytesReturned, NULL, NULL);
#else
        int keepAlive = 1;   // 開啟keepalive屬性. 缺省值: 0(關(guān)閉) 
        int keepIdle = idle_time;   // 如果在idle秒內(nèi)沒(méi)有任何數(shù)據(jù)交互,則進(jìn)行探測(cè). 缺省值:7200(s) 
        int keepInterval = interval_time;   // 探測(cè)時(shí)發(fā)探測(cè)包的時(shí)間間隔為interval秒. 缺省值:75(s) 
        int keepCount = 2;   // 探測(cè)重試的次數(shù). 全部超時(shí)則認(rèn)定連接失效..缺省值:9(次) 
        setsockopt(socket_id, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
#ifdef __linux__
        setsockopt(socket_id, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
#endif
        setsockopt(socket_id, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepInterval, sizeof(keepInterval));
        setsockopt(socket_id, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepCount, sizeof(keepCount));
#endif
    }

    bool SetNoBlock(int fd)
    {
#ifdef WIN32
        unsigned long flag = 1;
        if (ioctlsocket(fd, FIONBIO, (unsigned long *)&flag) < 0)
        {
            LOG << "ioctlsocket set fail";
            return false;
        }
        return true;
#else
        int flags;
        if ((flags = fcntl(fd, F_GETFL)) == -1)
        {
            LOG << "fcntl get fail";
            return false;
        }

        flags |= O_NONBLOCK; //設(shè)置非阻塞標(biāo)志位
        if (fcntl(fd, F_SETFL, flags) == -1)
        {
            LOG << "fcntl set fail";
            return false;
        }
        return true;
#endif
    }

    bool IsAlive(int socket_id) {
        int type = 0;
        socklen_t typesize = sizeof(type);
        int iCode = getsockopt(socket_id, SOL_SOCKET, SO_ERROR, (char*)&type, &typesize);
        return (iCode == 0);
    }

    // @params sock_type
    // TCP : SOCK_STREAM
    // UDP : SOCK_DGRAM
    int Connect(const std::string hostname_or_ip, uint16_t port, int sock_type)
    {
        addrinfo *answer, hint, *curr;
        int ret;
        memset(&hint, 0, sizeof(hint));
        hint.ai_family = AF_UNSPEC;// 也可以用AF_INET或AF_INET6指定ipv4或ipv6
        hint.ai_socktype = sock_type;

        if ((ret = getaddrinfo(hostname_or_ip.c_str(), std::to_string(port).c_str(), &hint, &answer)) != 0) {
            LOG << "getaddrinfo fail errno:" << LastErrorNo() << " errstr:" << LastErrorString()
                << " extra_info:" << gai_strerror(ret);
            return -1;
        }

        sockaddr_in  sockaddr_ipv4;
        sockaddr_in6 sockaddr_ipv6;

        bool bValid = false;
        bool bIpv6Flag = false;
        for (curr = answer; curr != NULL; curr = curr->ai_next)
        {
            switch (curr->ai_family)
            {
            case AF_INET:
            {
                auto* sockaddr_ipv4_ptr = reinterpret_cast<sockaddr_in *>(curr->ai_addr);
                memset(&sockaddr_ipv4, 0, sizeof(sockaddr_ipv4));
                sockaddr_ipv4.sin_family = curr->ai_family;
                sockaddr_ipv4.sin_addr = sockaddr_ipv4_ptr->sin_addr;
                sockaddr_ipv4.sin_port = htons(port);
                bValid = true;
            }
            break;
            case AF_INET6:
            {
                auto* sockaddr_ipv6_ptr = reinterpret_cast<sockaddr_in6 *>(curr->ai_addr);
                memset(&sockaddr_ipv6, 0, sizeof(sockaddr_ipv6));
                sockaddr_ipv6.sin6_family = curr->ai_family;
                sockaddr_ipv6.sin6_addr = sockaddr_ipv6_ptr->sin6_addr;
                sockaddr_ipv6.sin6_port = htons(port);
                bValid = true;
                bIpv6Flag = true;
            }
            break;
            }
            if (bValid)
            {
                break;
            }
        }
        freeaddrinfo(answer);

        if (!bValid)
        {
            LOG << "getaddrinfo does not get valid sock_addr";
            return -1;
        }

        int addr_af = bIpv6Flag ? AF_INET6 : AF_INET;
        sockaddr * addr_ptr = bIpv6Flag ? (sockaddr *)&sockaddr_ipv6 : (sockaddr *)&sockaddr_ipv4;
        int addr_len = bIpv6Flag ? sizeof(sockaddr_ipv6) : sizeof(sockaddr_ipv4);

        int socket_id = socket(addr_af, sock_type, 0);
        if (socket_id < 0) {
            LOG_NET_ERR_INFO;
            return -1;
        }

        if (connect(socket_id, addr_ptr, addr_len) < 0) {
            LOG_NET_ERR_INFO;
            CloseSocket(socket_id);
            return -1;
        }

        SetNoBlock(socket_id);
        
        // TCP
        if(sock_type == SOCK_STREAM)
        {
            const int on = 1;
            setsockopt(socket_id, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on));
        }

#ifndef WIN32 // 信號(hào)會(huì)引起崩潰
        {
            const int set = 1;
            setsockopt(socket_id, SOL_SOCKET, SO_NOSIGPIPE, (const void *)&set, sizeof(set));
        }
#endif
        return socket_id;
    }

    int Listen(uint16_t port, int sock_type, std::string _bindAddress = "")
    {
        const int on = 1;
        int ret;
        int socket_id = -1;

        sockaddr_in  sockaddr_ipv4;
        memset(&sockaddr_ipv4, 0, sizeof(sockaddr_ipv4));
        sockaddr_ipv4.sin_family = AF_INET;
        sockaddr_ipv4.sin_addr.s_addr = INADDR_ANY;
        sockaddr_ipv4.sin_port = htons(port);
        // bind address
        if (_bindAddress.length() > 0)
        {
            inet_pton(AF_INET, _bindAddress.c_str(), (void*)&sockaddr_ipv4.sin_addr);
        }

        socket_id = socket(AF_INET, sock_type, 0);
        if (socket_id < 0)
        {
            LOG_NET_ERR_INFO;
            return -1;
        }
        // 重復(fù)使用本地址與socket文件進(jìn)行綁定
        // 如果不設(shè)置系統(tǒng),會(huì)保留此連接直到最后一引用才釋放蚁阳,進(jìn)程結(jié)束后系統(tǒng)需要幾分鐘后才能重新進(jìn)行綁定
        setsockopt(socket_id, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
        SetNoBlock(socket_id);
        ret = bind(socket_id, (struct sockaddr*)&sockaddr_ipv4, sizeof(sockaddr_ipv4));
        if (ret != 0)
        {
            LOG_NET_ERR_INFO;
            CloseSocket(socket_id);
            return -1;
        }

        listen(socket_id, 50);

        return socket_id;
    }

    int Accept(int socket_id)
    {
        sockaddr_storage client;
        socklen_t client_len = sizeof(client);

        /* new client */
        int fd = accept(socket_id, (struct sockaddr *)&client, &client_len);
        return fd;
    }

}

// 定義了個(gè)簡(jiǎn)單報(bào)文協(xié)議铃绒,開頭兩個(gè)字節(jié)的長(zhǎng)度,| len(2) | data |
// 主要用于TCP
class CodeRingBuf {
public:
    CodeRingBuf()
    {
        _head = 0;
        _tail = 0;
    }

    int GetUsedBufLength()
    {
        return _tail - _head;
    }

    int GetFreeBufLength()
    {
        return BUF_REAL_LEN - GetUsedBufLength();
    }
    
    void Clear()
    {
        _head = _tail = 0;
    }

    // 輸出緩存相關(guān)函數(shù)
    bool CheckFreeBufEnough(int len)
    {
        return GetFreeBufLength() >= len;
    }

    inline void _PutInOneChar(uint8_t ch)
    {
        _buf[_tail&BUF_REAL_LEN] = ch;
        ++_tail;
    }

    void PutInOneCode(const char* buf, int len)
    {
        len += 2;// 加上兩個(gè)長(zhǎng)度字節(jié)
        assert(GetFreeBufLength() >= len);
        _PutInOneChar((len >> 8) & 0xff);
        _PutInOneChar(len & 0xff);
        len -= 2;
        for (int i = 0; i < len; ++i)
        {
            _PutInOneChar(buf[i]);
        }
    }

    int CopyInfo(char* out_buf, int len) {
        int copy_len = GetUsedBufLength();
        if (len < copy_len) copy_len = len;

        for (int i = 0; i < copy_len; ++i)
        {
            out_buf[i] = _buf[(_head + i)&BUF_REAL_LEN];
        }
        return copy_len;
    }

    void Consume(int len)
    {
        assert(GetUsedBufLength() >= len);
        _head += len;
    }

    void PutIn(const char* buf, int len)
    {
        assert(GetFreeBufLength() >= len);
        for (int i = 0; i < len; ++i)
        {
            _PutInOneChar(buf[i]);
        }
    }

    int GetOutOneCode(char* out_buf, int buf_len)
    {
        int used_buf = GetUsedBufLength();
        if (used_buf < 2)
        {
            return -1;
        }

        int len = _buf[_head&BUF_REAL_LEN] << 8;
        len |= _buf[(_head + 1)&BUF_REAL_LEN];
        if (len < 2 || len > BUF_REAL_LEN)
        {
            LOG << "protocol error len=" << len;
            Clear();
            return -1;
        }

        if (GetUsedBufLength() < len)
        {
            return -1;
        }

        len -= 2;
        if (buf_len < len) {
            assert(!"this situation should not happen");
            Clear();
            return -1;
        }
        _head += 2;
        for (int i = 0; i < len; ++i)
        {
            out_buf[i] = _buf[_head&BUF_REAL_LEN];
            ++_head;
        }
        return len;
    }

private:
    // 代碼有優(yōu)化
    // buf長(zhǎng)度一定要是2^n螺捐,有效長(zhǎng)度是2^n-1.
    // 如果總流量<2^32颠悬,_tail就是總流量
    const static int BUF_LEN = 1 << 16;
    const static int BUF_REAL_LEN = BUF_LEN - 1;
    uint8_t _buf[BUF_LEN];
    uint32_t _head;
    uint32_t _tail;
};

class NetConnect {
public:
    virtual ~NetConnect() {};

    virtual int Connect(const std::string hostname_or_ip, uint16_t port) = 0;
    virtual void SendOneCode(const char* msg, int len) = 0;
    virtual int RecvOneCode(char* buf, int len) = 0;

    // 測(cè)試用
    virtual void SendMsg(const std::string msg)
    {
        SendOneCode(msg.c_str(), msg.length());
    }
    virtual std::string RecvMsg() {
        char buf[1500] = {0};
        // 一定要讀到一組數(shù)據(jù)才結(jié)束
        while (RecvOneCode(buf, sizeof(buf)) < 0);
        return buf;
    }
};

class TCPConnect :public NetConnect {
public:
    TCPConnect() :_socket_id(-1), _is_breaked_by_server(false) {}

    TCPConnect& operator=(int socket_id)
    {
        _socket_id = socket_id;
        _is_breaked_by_server = false;
        return *this;
    }

    void LogInfo(std::string head)
    {
        LOG << head
            << " sock = " << NetUtils::GetLocalIP(_socket_id)
            << ":" << NetUtils::GetLocalPort(_socket_id)
            << " peer = " << NetUtils::GetPeerIP(_socket_id)
            << ":" << NetUtils::GetPeerPort(_socket_id);
    }

    int GetSocketId()
    {
        return _socket_id;
    }

    int Connect(const std::string hostname_or_ip, uint16_t port)
    {
        _send_buf.Clear();
        _recv_buf.Clear();
        _socket_id = NetUtils::Connect(hostname_or_ip, port, SOCK_STREAM);
        return _socket_id;
    }

    void SendOneCode(const char* msg, int len)
    {
        if (_send_buf.CheckFreeBufEnough(len + 2))
        {
            _send_buf.PutInOneCode(msg, len);
        }
        else
        {
            LOG << "buf overflow";
            return;
        }
        SendBuf();
    }

    void SendBuf()
    {
        if (_send_buf.GetUsedBufLength() > 0)
        {
            char buf[1024];
            int send_len,success_len;
            while ((send_len = _send_buf.CopyInfo(buf, sizeof(buf))) > 0)
            {
                success_len = send(_socket_id, buf, send_len, 0);
                if (success_len < 0)
                {
                    // todo(check errno)
                    break;
                }
                _send_buf.Consume(success_len);
            }
        }
    }

    int RecvOneCode(char* buf, int len)
    {
        // first read buf
        {
            int code_len = _recv_buf.GetOutOneCode(buf, len);
            if (code_len >= 0) {
                return code_len;
            }
        }

        for (;;)
        {
            char buf[1024];
            int recv_len;
            recv_len = recv(_socket_id, buf, len, 0);
            if (recv_len > 0)
            {
                if (_recv_buf.CheckFreeBufEnough(recv_len))
                {
                    _recv_buf.PutIn(buf, recv_len);
                }
                else
                {
                    LOG << "buf overflow";
                    // todo(reset connect)
                    break;
                }
                
            }
            else if (recv_len == 0)
            {
                // TCP recv()==0, connect is breaked
                break;
            }
            else if (recv_len < 0)
            {
                // todo(check errno)
                break;
            }
        }

        // last read buf
        {
            int code_len = _recv_buf.GetOutOneCode(buf, len);
            if (code_len >= 0) {
                return code_len;
            }
        }

        // get one code fail
        return -1;
    }

private:
    int _socket_id;
    bool _is_breaked_by_server;
    CodeRingBuf _send_buf;
    CodeRingBuf _recv_buf;
};

class UDPConnect :public NetConnect {
public:
    UDPConnect():_socket_id(-1) {}

    UDPConnect& operator=(int socket_id)
    {
        _socket_id = socket_id;
        return *this;
    }
    UDPConnect(int socket_id) : _socket_id(socket_id) {}

    int GetSocketId()
    {
        return _socket_id;
    }

    void LogInfo(std::string head)
    {
        LOG << head
            << " sock = " << NetUtils::GetLocalIP(_socket_id)
            << ":" << NetUtils::GetLocalPort(_socket_id);
    }

    int Connect(const std::string hostname_or_ip, uint16_t port)
    {
        _socket_id = NetUtils::Connect(hostname_or_ip, port, SOCK_DGRAM);
        return _socket_id;
    }

    void SendOneCode(const char* msg, int len)
    {
        // UDP不用分包
        int success_len = send(_socket_id, msg, len, 0);
        if (success_len < 0)
        {
            // todo(check errno)
        }
    }

    int RecvOneCode(char* buf, int len)
    {
        int success_len = recv(_socket_id, buf, len, 0);
        if (success_len < 0)
        {
            // todo(check errno)
            return -1;
        }
        return success_len;
    }
private:
    int _socket_id;
};

class TCPListenConnect {
public:
    TCPListenConnect():_socket_id(-1){}

    void LogInfo()
    {
        LOG << "socket_id:" << _socket_id
            << " listen on " << NetUtils::GetLocalIP(_socket_id)
            << ":" << NetUtils::GetLocalPort(_socket_id);
    }

    int GetSocketId()
    {
        return _socket_id;
    }

    int Listen(uint16_t port, std::string bind_address = "")
    {
        _socket_id = NetUtils::Listen(port, SOCK_STREAM, bind_address);
        return _socket_id;
    }

    int Accept()
    {
        return NetUtils::Accept(_socket_id);
    }
private:
    int _socket_id;
};

class UDPListenConnect {
public:
    UDPListenConnect():_socket_id(-1){}

    void LogInfo()
    {
        LOG << "socket_id:" << _socket_id
            << " listen on " << NetUtils::GetLocalIP(_socket_id)
            << ":" << NetUtils::GetLocalPort(_socket_id);
    }

    int GetSocketId()
    {
        return _socket_id;
    }

    int Listen(uint16_t port, std::string bind_address = "")
    {
        _socket_id = NetUtils::Listen(port, SOCK_DGRAM, bind_address);
        return _socket_id;
    }

    // 測(cè)試函數(shù),反射多少包才結(jié)束归粉,
    void ReflectALL(int num)
    {
        sockaddr_storage client;
        socklen_t client_len = sizeof(client);
        char buf[1024];
        int recv_len;
        for (;;)
        {
            recv_len = recvfrom(_socket_id, buf, sizeof(buf), 0, (sockaddr*)&client, &client_len);
            if (recv_len >= 0)
            {
                sendto(_socket_id, buf, recv_len, 0, (sockaddr*)&client, client_len);
                --num;
            }
            else
            {
                // todo(check errno)
                if (num <= 0)
                    break;
            }
        }
    }

private:
    int _socket_id;
};

int main() {
#ifdef WIN32
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        LOG << LastErrorNo() << LastErrorString();
        return 0;
    };
#endif

    std::string local_ip = "127.0.0.1";
#ifdef WIN32
    LOG << "local ip = " << NetUtils::GetLocalIPAddress();
#endif
    
    LOG_NEW_LINE;
    LOG << "TCP no block test start";
    {
        TCPListenConnect listen_connect;
        listen_connect.Listen(8080);
        listen_connect.LogInfo();
        TCPConnect client_connect;
        client_connect.Connect(local_ip, 8080);
        int id;
        TCPConnect server_connect;
        for (;;)
        {
            id = listen_connect.Accept();
            if (id > 0)
            {
                server_connect = id;
                break;
            }
        }
        client_connect.LogInfo("client");
        server_connect.LogInfo("server");

        client_connect.SendMsg("hello");
        LOG << "server recv: " << server_connect.RecvMsg();
        server_connect.SendMsg("hi");
        LOG << "client recv: " << client_connect.RecvMsg();
    }

    LOG_NEW_LINE;
    LOG << "UDP no block test start";
    {
        UDPListenConnect listen_connect;
        listen_connect.Listen(8080);
        listen_connect.LogInfo();
        UDPConnect client_connect;
        client_connect.Connect(local_ip, 8080);

        client_connect.LogInfo("client");

        client_connect.SendMsg("hello");

        listen_connect.ReflectALL(1);
        LOG << "client reflect: " << client_connect.RecvMsg();
    }

#ifdef WIN32
    WSACleanup();
#endif // WIN32
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末椿疗,一起剝皮案震驚了整個(gè)濱河市漏峰,隨后出現(xiàn)的幾起案子糠悼,更是在濱河造成了極大的恐慌,老刑警劉巖浅乔,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倔喂,死亡現(xiàn)場(chǎng)離奇詭異铝条,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)席噩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門班缰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人悼枢,你說(shuō)我怎么就攤上這事埠忘。” “怎么了馒索?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵莹妒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我绰上,道長(zhǎng)旨怠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任蜈块,我火速辦了婚禮鉴腻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘百揭。我一直安慰自己爽哎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布信峻。 她就那樣靜靜地躺著倦青,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盹舞。 梳的紋絲不亂的頭發(fā)上产镐,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天阔加,我揣著相機(jī)與錄音残吩,去河邊找鬼。 笑死猪钮,一個(gè)胖子當(dāng)著我的面吹牛获印,可吹牛的內(nèi)容都是我干的述雾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼兼丰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼玻孟!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起鳍征,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤黍翎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后艳丛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匣掸,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡趟紊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了碰酝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霎匈。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖送爸,靈堂內(nèi)的尸體忽然破棺而出铛嘱,到底是詐尸還是另有隱情,我是刑警寧澤袭厂,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布弄痹,位于F島的核電站,受9級(jí)特大地震影響嵌器,放射性物質(zhì)發(fā)生泄漏肛真。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一爽航、第九天 我趴在偏房一處隱蔽的房頂上張望蚓让。 院中可真熱鬧,春花似錦讥珍、人聲如沸历极。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)趟卸。三九已至,卻和暖如春氏义,著一層夾襖步出監(jiān)牢的瞬間锄列,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工惯悠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邻邮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓克婶,卻偏偏與公主長(zhǎng)得像筒严,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子情萤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容