简单的 Winsocket UDP 通信

本文详细介绍了使用UDP通信实现客户端服务器模式的socket编程过程,包括初始化、获取本地IP、创建socket、绑定地址、接收数据和发送数据等关键步骤。同时,通过实例展示了如何在客户端和服务器端进行数据传输。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

// 服务端
// 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值