// 服务端
// UDP.cpp : 定义控制台应用程序的入口点。
//1 为UDP通信,单例模式
//2 一些socket函数不兼容 预处理器定义里添加如下的宏
//_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SCL_SECURE_NO_DEPRECATE;
//3 UDP为非面向连接方式不需要listen 与accept
#include"stdafx.h"
#include"afxwin.h"
#include"WinSock2.h"
#include<iostream>
#include <ws2tcpip.h>
#pragma comment(lib,"WS2_32.lib")
using namespace std;
//日志 可以不看
bool WriteErrorLogFile(CString csData)// errorlog
{
char buff[256];
CString ErrorLog;
GetModuleFileName(NULL, (LPWSTR)buff, sizeof(buff));
cout << buff << endl;
ErrorLog = CString(buff);
ErrorLog += ErrorLog.Left(ErrorLog.ReverseFind('\\')) + "\\ErrorLog";
CString stemp;
CString sPathName;
SYSTEMTIME sys_time;
GetLocalTime(&sys_time);
sPathName = ErrorLog;
stemp.Format(L"%d%d%d", sys_time.wYear, sys_time.wMonth, sys_time.wDay);
sPathName += "\\" + stemp;
CreateDirectory(sPathName, NULL);
CStdioFile f;
if (!f.Open(sPathName, CStdioFile::modeCreate | CStdioFile::modeNoTruncate | CStdioFile::modeWrite | CStdioFile::typeText))
{
return false;
}
f.SeekToEnd();
stemp.Format(L"%02d:%02d:%02d:%s\n", sys_time.wHour, sys_time.wMinute, sys_time.wSecond, csData);
f.WriteString(stemp);
f.Flush();
f.Close();
return true;
}
class UDPSERVER
{//私有
UDPSERVER(char *ip, char*port) :m_IP(ip), m_Port(port)
{
m_srvSocket = INVALID_SOCKET;
//init
// Init();
}
UDPSERVER(UDPSERVER const &that)
{
}
public:
static UDPSERVER *Instance(char*ip,char*port)
{
if (!_instance)
{
_instance = new UDPSERVER(ip, port);
}
return _instance;
}
static bool Init()
{
WSADATA wsaData;
try {
if (WSAStartup(0X202, &wsaData))//If successful, the WSAStartup function returns zero
{
int err = WSAGetLastError();
WSACleanup();
return true;
}
}
catch (...)
{
cout << "WSAStartup error code:" << WSAGetLastError() << endl;
return false;
}
return true;
}
static bool GetLocalIp(char *szIP)// 获取IP
{
char szHostName[128];
ZeroMemory(szHostName, sizeof(szHostName));
if (gethostname(szHostName, sizeof(szHostName))== 0)//gethostname 函数要在WSAStartup后进行调用。。。
{
// cout << "szHostName = " <<szHostName<< endl;
HOSTENT*pHostEnt = gethostbyname(szHostName);
if (pHostEnt)
{
sprintf(szIP, "%d.%d.%d.%d", (pHostEnt->h_addr_list[0][0] & 0xff),
(pHostEnt->h_addr_list[0][1] & 0xff),
(pHostEnt->h_addr_list[0][2] & 0xff),
(pHostEnt->h_addr_list[0][3] & 0xff));
//cout<<szIP<<endl;
return true;
}
}
else
{
cout << "gethostname error code:" << WSAGetLastError()<<endl;
return false;
}
return true;
}
bool StartServer()
{
// create socket
m_srvSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (INVALID_SOCKET == m_srvSocket)
{
WSACleanup();
char errBuf[16];
WriteErrorLogFile(CString(itoa(WSAGetLastError(), errBuf, sizeof(errBuf))));
return false;
}
// address
pAddrServer.sin_family = AF_INET;
pAddrServer.sin_port = htons(atoi(m_Port));
pAddrServer.sin_addr.s_addr = inet_addr(m_IP);//or INADDR_ANY:接收任意IP发来的数据或指定一个IP发送数据
//bind
if (bind(m_srvSocket, (const sockaddr*)&pAddrServer, sizeof(SOCKADDR_IN)))
{
WSACleanup();
closesocket(m_srvSocket);
char errBuf[16];
WriteErrorLogFile(CString(itoa(WSAGetLastError(), errBuf, sizeof(errBuf))));
return false;
}
//jUDP no need to listen or accept
return true;
}
void RcvData(char*buff = NULL)
{
int nSrvAddrLen = sizeof(SOCKADDR);
char bufMessage[1024];
ZeroMemory(bufMessage, sizeof(bufMessage));
int nRetRcv = recvfrom(m_srvSocket, bufMessage, sizeof(bufMessage), 0, (sockaddr*)&pAddrServer, (int *)
&nSrvAddrLen);
/*recv,send,(TCP)从一个已连接的socket接收或发送数据
sendto,recvfrom(UDP/TCP):从一个已连接或未连接的socket接收或发送数据
当你对于数据报socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,
但该socket仍然是数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。*/
//不管是recv还是recvfrom,都有两种模式,阻塞和非阻塞,可以通过ioctl函数来设置
if (0 == nRetRcv)
{
WSACleanup();
closesocket(m_srvSocket);
char errBuf[16];
WriteErrorLogFile(CString(itoa(WSAGetLastError(), errBuf, sizeof(errBuf))));
}
cout << "the client:"<<inet_ntoa(pAddrServer.sin_addr)<<" say:" << bufMessage << endl;
}
void SentData(char*buff=NULL)
{
// sendto
SOCKADDR_IN clientAddr;
clientAddr.sin_family = AF_INET;
clientAddr.sin_port = htons((short)6666);//发送到的端口
clientAddr.sin_addr.s_addr = inet_addr(m_IP);
int nRetSent = sendto(m_srvSocket,"hello client",sizeof("hello client"),0, (SOCKADDR*)&clientAddr, sizeof(clientAddr));
if (0 == nRetSent)
{
WSACleanup();
closesocket(m_srvSocket);
char errBuf[16];
WriteErrorLogFile(CString(itoa(WSAGetLastError(), errBuf, sizeof(errBuf))));
}
}
private:
char *m_IP;
char *m_Port;
SOCKET m_srvSocket;
SOCKADDR_IN pAddrServer ;
static UDPSERVER* _instance;
};
UDPSERVER*UDPSERVER::_instance = NULL;
int main(int argc,char*argv[])
{
char szIP[16] = {};
UDPSERVER::Init();
if (false != UDPSERVER::GetLocalIp(szIP))
{
UDPSERVER* pUdpServer = UDPSERVER::Instance(szIP, "8888");
if (pUdpServer->StartServer())
{
while (1)
{
pUdpServer->RcvData();
pUdpServer->SentData();
}
}
else
{
delete pUdpServer;
pUdpServer = NULL;
}
}
system("pause");
return 0;
}
//客户端
#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include<iostream>
#pragma comment (lib, "Ws2_32.lib")
using namespace std;
int main(int argc, char **argv)
{
//1 初始化
WSADATA wsaData;
SOCKET sServer;
WSAStartup(0x202, &wsaData);
//2 创建绑定socket
sServer = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN srvAddr;
srvAddr.sin_family = AF_INET;
srvAddr.sin_port = htons(6666);
srvAddr.sin_addr.s_addr = inet_addr("172.168.28.201");
bind(sServer, (const sockaddr*)&srvAddr, sizeof(srvAddr));
//3 准备数据传输目的地的地址与数据
int nSrvAddrLen = sizeof(srvAddr);
char szSendBuff[1024];
ZeroMemory(szSendBuff, sizeof(szSendBuff));
memcpy(szSendBuff, "hello server", sizeof("hello server"));
SOCKADDR_IN cltAddr;
cltAddr.sin_family = AF_INET;
cltAddr.sin_port = htons(8888);// 发送到哪个端口 htons主机字节顺序转变成网络字节顺序
cltAddr.sin_addr.s_addr = inet_addr("172.168.28.201");//inet_addr 字符串转换为32位二进制网络字节序的IPV4地址
int nCltAddrLen = sizeof(cltAddr);
// 发送
sendto(sServer, szSendBuff, sizeof(szSendBuff), 0, (const sockaddr*)&cltAddr, nCltAddrLen);
// 接收
memset(szSendBuff, 0, sizeof(szSendBuff));
recvfrom(sServer, szSendBuff, sizeof(szSendBuff), 0, ( sockaddr*)&srvAddr, &nSrvAddrLen);
/*inet_pton inetntop 可以在将IP地址在“点分十进制”和“网络二进制结构”之间转换。而且,这2个函数能够处理ipv4和ipv6。算是比较新的函数*/
struct protoent *pe = getprotobyname("udp");
//getpeername(sServer, (sockaddr*)&srvAddr, &nSrvAddrLen);
cout << "the client receive the message: " << endl;
cout << "/* the server ip:" << inet_ntoa(srvAddr.sin_addr) << endl;//inet_ntoa 将网络地址转换成“.”点隔的字符串格式
cout << " the server port is " <<ntohs(srvAddr.sin_port) << endl;//htons
cout << " the server protocol:" << pe->p_name << endl;
cout << " the server protocal num is " << pe->p_proto << endl;
cout << " the server message is : "<< szSendBuff << " */ " << endl;
system("pause");
return 0;
}