代码示例展示了关于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;
}