C++——UDP的socket通信

1.UDP流程

UDP特点:基于报文、不保证按序发送、一对一,一对多,多对一和多对多的交互通信、不可靠性(容易出现丢包现象)。

服务端: 创建socket-->bind-->循环接收/发送数据-->关闭socket

客户端:创建socket-->bind(可选)-->循环发送/接收数据-->关闭socket

流程图:


  UDP编程的服务端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、bind绑定IP地址、端口等信息到socket上; 
  4、循环接收数据,用函数recvfrom(); 
  5、关闭网络连接; 

UDP编程的客户端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、bind绑定IP地址、端口等信息到socket上;* 可选 
  4、设置对方的IP地址和端口等属性; 
  5、发送数据,用函数sendto(); 
  6、关闭网络连接;

2.举例

UDP.h

#pragma once
#include <Winsock2.h>


// UDP服务端
class CUDPServer
{
public:
	CUDPServer()
	{
		m_terminal = false;
	}
	~CUDPServer() {}

	// 服务端
	long UDPServer();

	// 接收客户端数据
	long RecvClientData();

private:
	char recvClientBuf[100];
	SOCKET sockSrv;
	SOCKADDR_IN addrClient;	//用来接收客户端的地址信息
	bool m_terminal;
};

// UDP客户端
class CUDPClient
{
public:
	CUDPClient() {}
	
	~CUDPClient() {}

	// 客户端
	long UDPClient();

	// 接收服务端数据
	long RecvServerData();
private:
	char recvServerBuf[100];
	SOCKET sockClient;
	SOCKADDR_IN addrSrv;
};

UDP.cpp

#include "stdafx.h"
#include "UDP.h"
#include <iostream>
#include <thread>

#pragma comment( lib, "ws2_32.lib" ) 


// 服务端
long CUDPServer::UDPServer()
{
	//初始化winsocket
	WORD wVersionRequested;
	WSADATA wsaData;

	int nPort = 8888;	//端口号

    //第一个参数为低位字节;第二个参数为高位字节
	wVersionRequested = MAKEWORD(1, 1);	//声明调用不同的Winsock版本。例如MAKEWORD(2,2)就是调用2.2版,MAKEWORD(1,1)就是调用1.1版
	int err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		return -1;
	}

	//对winsock DLL初始化
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		WSACleanup();
		return -1;
	}

	//1、创建一个socket,用函数socket()
	sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockSrv < 0)
	{
		printf("udp server creat socket error\n");
		return -1;
	}
	printf("udp server creat socket success\n");

	//2、设置socket属性,用函数setsockopt(); 可选 
	SOCKADDR_IN local;	 //定义sockSrv发送和接收数据包的地址
	local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);	//htonl用来将主机字节顺序转换为网络字节顺序(to network long) INADDR_ANY就是指定地址为0.0.0.0的地址
	local.sin_family = AF_INET;						//设置为IP通信
	local.sin_port = htons(nPort);					//服务器端口号

	//3、绑定IP地址、端口等信息到socket上,用函数bind()
	int len = sizeof(SOCKADDR);
	int nRes = bind(sockSrv, (SOCKADDR*)&local, len);
	if (nRes < 0)
	{
		printf("udp server bind IP error\n");
		return -1;
	}
	printf("udp server bind success\n");

	printf("udp server accept client ip is:[%s]\n", inet_ntoa(local.sin_addr));
	printf("udp server to client sent data, please input:\n");

	// 4、循环等待接收数据,用函数recvfrom()
	std::thread th1(std::bind(&CUDPServer::RecvClientData, this));
	th1.detach();

	char buffs[100];
	while (!m_terminal)
	{
		//sendto发送数据
		memset(buffs, 100, 0);
		std::cin.getline(buffs, 100);
		if (0 == std::strcmp(buffs, "q"))	//服务端退出
		{
			printf("udp server quit...\n");
			sendto(sockSrv, buffs, strlen(buffs) + 1, 0, (SOCKADDR*)&addrClient, len);
			m_terminal = true;
			break;
		}
		sendto(sockSrv, buffs, strlen(buffs) + 1, 0, (SOCKADDR*)&addrClient, len);
	}

	// 5、关闭网络连接
	closesocket(sockSrv);
	printf("udp server close socket...\n");
	WSACleanup();
	return 0;
}

long CUDPServer::RecvClientData()
{
	int len = sizeof(SOCKADDR);
	while (!m_terminal) {
		memset(recvClientBuf, 100, 0);
		int nLen = recvfrom(sockSrv, recvClientBuf, 100, 0, (SOCKADDR*)&addrClient, &len);
		if (nLen <= 0)	//检测客户端退出
		{
			printf("client connect close, please check[%d]....\n", nLen);
			break;
		}

		if (0 == std::strcmp(recvClientBuf, "q"))	//判断客户端退出
		{
			printf("udp client quit...\n");
			continue;
		}
		printf("%s\n", recvClientBuf);
	}
	return 0;
}

// 客户端
long CUDPClient::UDPClient()
{
	//初始化winsocket
	WORD wVersionRequested;
	WSADATA wsaData;

	int nPort = 8888;	//端口号

	//第一个参数为低位字节;第二个参数为高位字节
	wVersionRequested = MAKEWORD(1, 1);	//声明调用不同的Winsock版本。例如MAKEWORD(2,2)就是调用2.2版,MAKEWORD(1,1)就是调用1.1版
	int err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		return -1;
	}

	//对winsock DLL初始化
	if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {
		WSACleanup();
		return -1;
	}

	//1、创建一个socket,用函数socket()
	sockClient = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockClient < 0)
	{
		printf("udp client creat socket error\n");
		return -1;
	}

	//2、设置socket属性,用函数setsockopt(); 可选 
	//SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(nPort);

	printf("udp cilent accept server ip is:[%s]\n", inet_ntoa(addrSrv.sin_addr));
	printf("udp cilent to server sent data, please input:\n");

	int len = sizeof(SOCKADDR);
	//发送信息与服务器建立连接(必须加)
	sendto(sockClient, "begin connect server", strlen("begin connect server"), 0, (SOCKADDR*)&addrSrv, len);

	//接收数据,线程接收数据
	std::thread th1(std::bind(&CUDPClient::RecvServerData, this));
	th1.detach();

	//char recvBuf[100];
	while (true)
	{
		//3、发送数据,用函数sendto()
		char buffs[100];
		std::cin.getline(buffs, 100);
		if (0 == std::strcmp(buffs, "q"))	//客户端主动退出
		{
			printf("udp client quit\n");
			sendto(sockClient, buffs, strlen(buffs) + 1, 0, (SOCKADDR*)&addrSrv, len);
			break;
		}
		int nSent = sendto(sockClient, buffs, strlen(buffs) + 1, 0, (SOCKADDR*)&addrSrv, len);
	}

	//4、关闭网络连接;
	closesocket(sockClient);
	WSACleanup();
	return 0;
}

long CUDPClient::RecvServerData()
{
	int len = sizeof(SOCKADDR);
	while (true) {
		memset(recvServerBuf, 100, 0);
		int nRes = recvfrom(sockClient, recvServerBuf, 100, 0, (SOCKADDR*)&addrSrv, &len);
		if (nRes <= 0)	//检测客户端退出
		{
			printf("udp client connect close, please check....\n");
			break;
		}

		if (0 == std::strcmp(recvServerBuf, "q"))	//判断服务端退出
		{
			printf("udp server quit...\n");
			continue;
		}
		printf("%s\n", recvServerBuf);
	}
	return 0;
}

main 调用:

int main()
{
	//测试UDP客户端
	std::shared_ptr<CUDPClient> pUDPClient = std::make_shared<CUDPClient>();
	pUDPClient->UDPClient();

    //测试UDP服务端
	//std::shared_ptr<CUDPServer> pUDPServer = std::make_shared<CUDPServer>();
	//pUDPServer->UDPServer();
}

 运行结果:

发布了84 篇原创文章 · 获赞 2 · 访问量 5243

猜你喜欢

转载自blog.csdn.net/finghting321/article/details/105069341