// udpsocket.cpp
//
#include "stdafx.h"
#include "udpsocket.h"
#define MAXDELAYTIME 10 // 最大延时10秒
#define PACKHEAD_LEN 22
#define MAX_LEN 1024
CUDPSocket m_UDPSocket;
CUDPSocket::CUDPSocket()
{
m_bInit = TRUE;
m_hSocket = NULL;
}
CUDPSocket::~CUDPSocket()
{
WSACleanup();
}
/* ==============
初始化
参数:
无
返回值:
0 成功.
-1 失败,在返回1以前,显示错误原因。
*///=============
int CUDPSocket::InitSocket(int port)
{
WSADATA wsaData;
WORD version = MAKEWORD(2,2);
int ret = WSAStartup(version, &wsaData);
/*CString aa;
char a[10];
aa = _itoa(ret, a, 10);
AfxMessageBox(aa);*/
if (ret != 0)
{
TRACE("Initilize Error!\n");
m_bInit = FALSE;
return -1;
}
// 创建新套接字
m_hSocket = socket(AF_INET, SOCK_DGRAM, 0);
ASSERT(m_hSocket != NULL);
// 填写服务器地址信息
memset(&sockAddr, 0x00, sizeof(sockaddr_in));
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(port);
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
// 绑定监听端口
ret = bind(m_hSocket, (SOCKADDR *)&sockAddr, sizeof(sockAddr));
if (ret == SOCKET_ERROR)
{
ErrorCode2Text((DWORD)WSAGetLastError());
m_hSocket = NULL;
return -2;
}
return ret;
}
/* ==============
函数功能:检测套接字是否可读
参数:
无
返回值:
0 可读
其他数值不可读
*///=============
int CUDPSocket::CheckSocketReadable()
{
fd_set fdset;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 5;
FD_ZERO(&fdset);
FD_SET(m_hSocket,&fdset);
int ret = select(m_hSocket+1, &fdset, NULL, NULL, &tv);
if (ret == 0)
{
return -1;
}
return 0;
}
/* ==============
函数功能:检测套接字是否可写
参数:
无
返回值:
0 可写
其他数值不可写
*///=============
int CUDPSocket::CheckSocketWriteable()
{
fd_set fdset;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 5;
FD_ZERO(&fdset);
FD_SET(m_hSocket,&fdset);
int ret = select(m_hSocket+1, NULL, &fdset, NULL, &tv);
if (ret == 0)
{
return -1;
}
return 0;
}
/* ==============
客户端发送消息函数 广播方式
参数:
buff:发送的数据缓冲区。
返回值:
0 成功.
1 失败
*///=============
int CUDPSocket::SendMsg(char *buff, USHORT len, UINT port)
{
ASSERT(buff != NULL);
if (!m_bInit)
return -1;
BOOL fBroadcast = TRUE;
if (setsockopt(m_hSocket, SOL_SOCKET, SO_BROADCAST, (char *)&fBroadcast, sizeof(fBroadcast)) == SOCKET_ERROR)
{
return -1;
}
sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int end = sendto(m_hSocket, (char *)buff, len, 0, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
if (end == SOCKET_ERROR)
{
ErrorCode2Text((DWORD)WSAGetLastError());
return -1;
}
return 0;
}
/* ==============
客户端发送消息函数
参数:
buff:发送的数据缓冲区。
返回值:
0 成功.
1 失败
*///=============
int CUDPSocket::SendMsg(char *buff, USHORT len, char* ip, UINT port)
{
ASSERT(buff != NULL);
if (!m_bInit)
return -1;
BOOL fBroadcast = FALSE;
if( setsockopt(m_hSocket, SOL_SOCKET, SO_BROADCAST, (char*)&fBroadcast, sizeof(fBroadcast)) == SOCKET_ERROR)
{
return -1;
}
sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.S_un.S_addr = inet_addr(ip);
int end = sendto(m_hSocket, (char *)buff, len, 0, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
if (end == SOCKET_ERROR)
{
ErrorCode2Text((DWORD)WSAGetLastError());
return -2;
}
else if (end != len)
{
return -3;
}
return 0;
}
/* ==============
客户端接收消息函数
参数:
buff:接收数据的缓冲区,填满实参所指缓冲区,
注意,实参应是已经分配好空间的缓冲区。
返回值:
0 成功.
1 失败,在返回1以前,显示错误原因。
*///=============
int CUDPSocket::ReceiveMsg(char *buff, int &len, char* ip)
{
ASSERT(buff != NULL);
if (!m_bInit)
return 1;
sockaddr_in myaddr;
int nAddrSize = sizeof(sockaddr_in);
int iTotal = 0; // 总接收字节数
int iRet = 0; // 接收字节
time_t startTime = time(NULL);
while (1)
{
// 收取包头标志4A 79
iRet = recvfrom(m_hSocket, buff+iTotal, 1, 0, (SOCKADDR *)&myaddr, &nAddrSize);
if (*(char*)buff == 0x4A)
{
iTotal = 1;
iRet = recvfrom(m_hSocket, buff+iTotal, 1, 0, (SOCKADDR *)&myaddr, &nAddrSize);
if (*(char*)(buff+1) == 0x79)
{
iTotal = 2;
break;
}
}
iTotal = 0;
if (time(NULL) - startTime > MAXDELAYTIME)
{
AfxMessageBox("报文头不对\n");
return -2;
}
}
// 收取包头
iRet = recvfrom(m_hSocket, buff+iTotal, PACKHEAD_LEN - 2, 0, (SOCKADDR *)&myaddr, &nAddrSize);
iTotal = iTotal + iRet;
USHORT nDataLen = *(USHORT*)(buff+PACKHEAD_LEN-2); // 数据长度
if (nDataLen > MAX_LEN)
{
printf("数据长度不对\n");
return -3;
}
startTime = time(NULL);
iRet = 0;
int nCheck = 0;
while (1)
{
iRet = recvfrom(m_hSocket, buff+iTotal, 2+nDataLen-nCheck, 0, (SOCKADDR *)&myaddr, &nAddrSize); // 收取数据和校验码
iTotal = iTotal + iRet;
nCheck += iRet;
if (nCheck == nDataLen + 2)
{
break;
}
if (time(NULL) - startTime > 5)
{
printf("包体长度不够\n");
return -4;
}
}
strcpy(ip, inet_ntoa(myaddr.sin_addr));
return 0;
}
/* ==============
关闭Socket连接
返回值:
无
参数:
无
*///=============
void CUDPSocket::CloseClientSocket()
{
closesocket(m_hSocket);
m_hSocket = NULL;
}
/* ==============
获取错误代码函数
参数:
dw 错误代码编号
返回值:
错误内容
*///=============
CString CUDPSocket::ErrorCode2Text(DWORD dw)
{
CString error = "";
// Put your own common error text here ()
switch(dw)
{
case WSANOTINITIALISED:
error = "A successful WSAStartup call must occur before using this function.";
break;
case WSAENETDOWN:
error = "The network subsystem has failed.";
break;
case WSAEFAULT:
error = "The lpBuffers, lpFlags, lpFrom, lpNumberOfBytesRecvd, lpFromlen, lpOverlapped, or lpCompletionRoutine parameter is not totally contained in a valid part of the user address space: the lpFrom buffer was too small to accommodate the peer address.";
break;
case WSAEINTR:
error = "A blocking Windows Socket 1.1 call was canceled through WSACancelBlockingCall.";
break;
case WSAEINPROGRESS:
error = "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.";
break;
case WSAEINVAL:
error = "The socket has not been bound (with bind, for example).";
break;
case WSAEISCONN:
error = "The socket is connected. This function is not permitted with a connected socket, whether the socket is connection-oriented or connectionless.";
break;
case WSAENETRESET:
error = "The connection has been broken due to keep-alive activity detecting a failure while the operation was in progress.";
break;
case WSAENOTCONN:
error = "The socket is not connected (connection-oriented sockets only).";
break;
case WSAEOPNOTSUPP:
error = "MSG_OOB was specified, but the socket is not stream-style such as type SOCK_STREAM, OOB data is not supported in the communication domain associated with this socket, or the socket is unidirectional and supports only send operations.";
break;
case WSAESHUTDOWN:
error = "The socket has been shut down; it is not possible to WSARecvFrom on a socket after shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH.";
break;
case WSAEWOULDBLOCK:
error = "Overlapped sockets: There are too many outstanding overlapped I/O requests. Nonoverlapped sockets: The socket is marked as nonblocking and the receive operation cannot be completed immediately";
break;
case WSAEMSGSIZE:
error = "The message was too large to fit into the specified buffer and (for unreliable protocols only) any trailing portion of the message that did n