同一端口监听TCP与UDP数据包

代码示例展示了关于udp与tcp在同一端口下的监听实现,具体的阻塞问题在网络模型里面有实现,可以查阅我提到的网络模型。这里主要是解决大家对端口绑定的疑惑。

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include<winsock2.h>
#include<windows.h>
#include <list>
#pragma comment(lib,"Ws2_32.lib")
using namespace std;

#define MAX_RECV 1024
enum _PORT
{
    PORT = 6000 // server port
};

typedef struct //存储tcp连接的客户端的信息
{
    SOCKET *sock;
    SOCKADDR_IN addr;
}USER;
list<USER*> user_list;
list<USER*>::iterator iter;

SOCKET tcpSock;
SOCKET udpSock;
SOCKADDR_IN addrServ;

bool InitNet();// init network

bool InitTcpServer();// init tcp server

bool InitUdpServer();// init udp server

bool BeginTcpAccept();//  TCP begin accept

void Clean();// clean

DWORD WINAPI UdpServerRecvProc(LPVOID lparam); // udp server recvfrom thread

DWORD WINAPI TcpServerRecvProc(LPVOID lparam); // tcp server recv thread

int main()
{
    if (!InitNet())
        return -1;

    if (!InitTcpServer())
        return -1;

    if (!InitUdpServer())
        return -1;

    CreateThread(NULL, NULL, TcpServerRecvProc, NULL, NULL, NULL);
    CreateThread(NULL, NULL, UdpServerRecvProc, (LPVOID)&udpSock, NULL, NULL);

    BeginTcpAccept();

    Clean(); 

    return 0;
}


bool InitNet()
{
    WSAData wsa;
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
    {
        printf("WSAStartup error:%d", WSAGetLastError());
        WSACleanup();
        return false;
    }

    return true;
}


bool InitTcpServer()
{
    tcpSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (INVALID_SOCKET == tcpSock)
    {
        printf("tcpSock init error:%d", WSAGetLastError());
        WSACleanup();
        closesocket(tcpSock);
        return false;
    }

    addrServ.sin_family = AF_INET;
    addrServ.sin_port = htons(PORT);
    addrServ.sin_addr.S_un.S_addr = htonl(ADDR_ANY);

    int ret = bind(tcpSock, (sockaddr*)&addrServ, sizeof(addrServ));
    if (SOCKET_ERROR == ret)
    {
        printf("tcpSock bind error:%d", WSAGetLastError());
        WSACleanup();
        closesocket(tcpSock);
        return false;
    }

    ret = listen(tcpSock, SOMAXCONN);
    if (SOCKET_ERROR == ret)
    {
        printf("tcpSock listen error:%d", WSAGetLastError());
        WSACleanup();
        closesocket(tcpSock);
        return false;
    }

    return true;
}


bool InitUdpServer()
{
    udpSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (INVALID_SOCKET == udpSock)
    {
        printf("udpSock init error:%d\n", WSAGetLastError());
        WSACleanup();
        closesocket(udpSock);
        return false;
    }

    int ret = bind(udpSock, (sockaddr*)&addrServ, sizeof(addrServ));
    if (SOCKET_ERROR == ret)
    {
        printf("udp bind error:%d\n", WSAGetLastError());
        WSACleanup();
        closesocket(udpSock);
        return false;
    }

    return true;
}


bool BeginTcpAccept()
{
    SOCKADDR_IN addrClient = { 0 };
    int len = sizeof(SOCKADDR_IN);

    while (TRUE)
    {
        SOCKET *sockClient = new SOCKET;
        *sockClient = accept(tcpSock, (sockaddr*)&addrClient, &len);
        if (INVALID_SOCKET == *sockClient)
        {
            printf("accpet() error:%d", WSAGetLastError());
            WSACleanup();
            closesocket(tcpSock);
            return false;
        }

        printf("TCP_MSG  [%s:%d]->log on\n", inet_ntoa(addrClient.sin_addr), htons(addrClient.sin_port));
        USER *user = new USER;
        user->sock = sockClient;
        user->addr = addrClient;
        user_list.push_back(user);
    }

    return true;
}


void Clean()
{
    WSACleanup();
    closesocket(tcpSock);
    closesocket(udpSock);
}


DWORD WINAPI UdpServerRecvProc(LPVOID lparam)
{
    char buf[MAX_RECV];
    SOCKET* udpSer = (SOCKET*)lparam;
    SOCKADDR_IN udpSrcAddr;
    ZeroMemory(&udpSrcAddr, sizeof(udpSrcAddr));
    int nLen = sizeof(udpSrcAddr);

    while (true)
    {
        memset(buf, '\0', MAX_RECV);
        int iRet = recvfrom(*udpSer, buf, MAX_RECV, 0, (sockaddr*)&udpSrcAddr, &nLen);
        if (SOCKET_ERROR == iRet)
        {
            printf("recvfrom() error + %d", GetLastError());
            continue;
        }
        printf("UDP_MSG  [%s:%d]:%s\n", 
            inet_ntoa(udpSrcAddr.sin_addr), 
            htons(udpSrcAddr.sin_port),
            buf);
    } 

    return 0;
}


DWORD WINAPI TcpServerRecvProc(LPVOID lparam)
{
    int addr_len = sizeof(SOCKADDR_IN);
    char recvBuf[MAX_RECV];

    while (true)
    {
        if (user_list.size() == 0)
        {
            Sleep(30);
            continue;
        }
        for (iter = user_list.begin(); iter != user_list.end(); ++iter)
        {
            memset(recvBuf, 0, MAX_RECV);
            // 此处的阻塞问题,导致只能 依次 接收链表中套接字发来的数据
            // 解决recv阻塞的问题,请阅读我关于 网络模型 的博文
            int ret = recv(*((*iter)->sock), recvBuf, MAX_RECV, 0);
            if (0 == ret)
            {
                printf("TCP_MSG  [%s:%d]->log off\n", 
                    inet_ntoa((*iter)->addr.sin_addr),
                    htons((*iter)->addr.sin_port));
                closesocket(*(*iter)->sock);
                iter = user_list.erase(iter); // 从链表中删除
                if (user_list.size() == 0)
                    break;
                continue;
            }

            if (SOCKET_ERROR == ret)
            {
                printf("recv() error + %d", WSAGetLastError());
                WSACleanup();
                closesocket(*(*iter)->sock);
                return -1;
            }
            printf("TCP_MSG  [%s:%d]:%s\n", 
                inet_ntoa((*iter)->addr.sin_addr), 
                htons((*iter)->addr.sin_port), 
                recvBuf);
        }
    }

    return 0;
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页