C++ socket编程 实现服务端与客户端的双向UDP通讯

运行环境:VS2015

TCP双向通信的代码及介绍可以见 C++ socket编程 实现服务端与客户端的双向TCP通讯

UDP通信流程

服务器端步骤:

  1. 加载套接字库,创建套接字(WSAStartup()/socket());
  2. 绑定套接字到一个IP地址和一个端口上(bind());
  3. 循环用返回的套接字和客户端进行通信(sendto()/recvfrom());
  4. 关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

客户端步骤:

  1. 加载套接字库,创建套接字(WSAStartup()/socket());
  2. 和服务器端进行通信(sendto()/recvfrom());
  3. 关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

github代码:https://github.com/jiangxinyiba/NetworkCommu_demo/tree/master

服务端代码Server.cpp:

#include "winsock2.h"
#include 
#include 
#pragma comment(lib, "ws2_32.lib")
using namespace std;

int main(int argc, char* argv[])
{
	const int BUF_SIZE = 64;
	WSADATA			wsd;			    //WSADATA变量
	SOCKET			sServer;		    //服务器套接字
	SOCKET			sClient;		    //客户端套接字
	SOCKADDR_IN		servAddr;		    //服务器地址
	SOCKADDR_IN     clientAddr;         //客户端地址
	int				nAddrLen = sizeof(clientAddr);
	char			bufSend[BUF_SIZE];	//发送数据缓冲区
	char			bufRecv[BUF_SIZE];  //接收数据缓冲区
	int				retVal;			    //返回值
	char*			closeSymbol = "0";  //结束通信的标志

										// 服务端套接字地址 
	servAddr.sin_family = AF_INET;        //协议
	servAddr.sin_port = htons(4999);      //端口
	inet_pton(AF_INET, "127.0.0.1", (void*)&servAddr.sin_addr.S_un.S_addr);

	// 初始化套接字动态库	
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup failed !" << endl;
		return 1;
	}

	// 创建服务端套接字
	sServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == sServer)
	{
		cout << "socket failed!" << endl;
		WSACleanup();			 //释放套接字资源;
		return  -1;
	}
	else
	{
		cout << "Server UDP Socket init!" << endl;
		cout << "Server UDP Socket IP: 127.0.0.1" << endl;
		cout << "Server UDP Socket Port: 4999" << endl;
	}

	// 套接字绑定IP和端口
	if (SOCKET_ERROR == bind(sServer, (LPSOCKADDR)&servAddr, sizeof(SOCKADDR_IN)))
	{
		cout << "bind failed!" << endl;
		closesocket(sServer);	//关闭服务端套接字
		WSACleanup();			//释放套接字资源;
		return -1;
	}
	else
	{
		cout << "Server UDP Socket bind IP & Port !" << endl;
	}

	// 循环等待其他端口发送数据,从客户端接收数据 & 向客户端发送数据
	while (true) {
		// 初始化缓冲空间
		ZeroMemory(bufRecv, BUF_SIZE);

		// 接收客户端发送的buf信息
		retVal = recvfrom(sServer, bufRecv, BUF_SIZE, 0, (sockaddr *)&clientAddr, &nAddrLen);
		if (SOCKET_ERROR == retVal)
		{// 接收失败则关闭服务端客户端套接字
			cout << "Recv Failed!" << endl;
			closesocket(sServer);	//关闭服务端套接字
			WSACleanup();			//释放套接字资源;
			return -1;
		}

		// 确认客户端发送的信息
		bufRecv[retVal] = '\0';			// 接收的最后一位设为\0,避免烫烫的问题
		char str[INET_ADDRSTRLEN];
		cout << "Client IP:" << inet_ntop(AF_INET, &clientAddr.sin_addr, str, sizeof(str)) << endl;
		cout << "Data recv from Client UDP Socket: " << bufRecv << endl;

		// 若客户端发送的数据是'0',则表示客户端想结束此次TCP通信		
		if (!strcmp(bufRecv, closeSymbol))
		{
			cout << "Client UDP Socket wants to finish this communication" << endl;
			break;
		}

		// 将sendBuf信息发送到客户端
		cout << "Data send to Client UDP Socket: ";
		cin >> bufSend;
		sendto(sServer, bufSend, strlen(bufSend), 0, (sockaddr *)&clientAddr, nAddrLen);
		// 若服务端发送的数据是'0',则表示服务端想结束此次TCP通信	
		if (!strcmp(bufSend, closeSymbol))
		{
			cout << "Server UDP Socket wants to finish this communication" << endl;
			break;
		}
	}
	// 退出
	closesocket(sServer);	//关闭服务端套接字
	WSACleanup();			//释放套接字资源;
	Sleep(5000);
	return 0;
}

客户端代码Client.cpp:

// ClientUDP.cpp : Defines the entry point for the console application.
#include "winsock2.h"
#include 
#include 
#pragma comment(lib, "ws2_32.lib")

#define _WINSOCK_DEPRECATED_NO_WARNINGS

using namespace std;

int main(int argc, char* argv[])
{
	const int		BUF_SIZE = 64;
	WSADATA			wsd;				//WSADATA变量
	SOCKET			sClient;			//客户端套接字
	SOCKADDR_IN		servAddr;			//服务器地址
	char			bufSend[BUF_SIZE];	//发送数据缓冲区
	char			bufRecv[BUF_SIZE];  //接收数据缓冲区
	int				retVal;				//返回值
	char*			closeSymbol = "0";//结束通信的标志

									  // 设置服务端地址
	servAddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", (void*)&servAddr.sin_addr.S_un.S_addr);
	servAddr.sin_port = htons((short)4999);
	int	nServAddlen = sizeof(servAddr);

	// 初始化套接字动态库
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup failed!" << endl;
		return -1;
	}

	// 创建服务端套接字
	sClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == sClient)
	{
		cout << "Socket failed !" << endl;
		WSACleanup();               //释放套接字资源
		return  -1;
	}
	else
	{
		cout << "Client UDP Socket init !" << endl;
	}

	// 循环等待,向服务端发送数据 & 从服务端接收数据
	while (true) 
	{
		// 初始化buf空间
		ZeroMemory(bufSend, BUF_SIZE);

		// 向服务端发送数据buf
		cout << "Data send to Server UDP Socket: ";
		cin >> bufSend;
		retVal = sendto(sClient, bufSend, strlen(bufSend), 0, (sockaddr *)&servAddr, nServAddlen);
		if (SOCKET_ERROR == retVal)
		{
			cout << "send failed!" << endl;
			closesocket(sClient);	//关闭服务端套接字
			WSACleanup();		    //释放套接字资源
			return -1;
		}
		// 若客户端发送的数据是'0',则表示客户端想结束此次TCP通信
		if (!strcmp(bufSend, closeSymbol))
		{
			cout << "Client UDP Socket wants to finish this communication" << endl;
			break;
		}

		// 从服务端接收数据bufRecv
		retVal = recvfrom(sClient, bufRecv, BUF_SIZE, 0, (sockaddr *)&servAddr, &nServAddlen);
		bufRecv[retVal] = '\0';
		char str[INET_ADDRSTRLEN];
		cout << "Server IP:" << inet_ntop(AF_INET, &servAddr.sin_addr, str, sizeof(str)) << endl;
		cout << "Data recv from Server UDP Socket: " << bufRecv << endl;
		// 若服务端发送的数据是'0',则表示服务端想结束此次TCP通信	
		if (!strcmp(bufRecv, closeSymbol))
		{
			cout << "Server UDP Socket wants to finish this communication" << endl;
			break;
		}

	}
	//退出
	closesocket(sClient);	//关闭服务端套接字
	WSACleanup();
	//释放套接字资源
	Sleep(5000);
	return 0;
}

C++ socket编程 实现服务端与客户端的双向UDP通讯_第1张图片


注意事项

  1. 大体代码和TCP方式是相同的。但是UDP因为追求通信的实时性,减少了许多环节。

  2. sClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 这里需要采用SOCK_DGRAM数据报方式,协议选择UDP。

  3. 在UDP协议中,recv和send的第一个参数的逻辑和TCP中有些区别。recv就是接收方的socket,send就是发送方的socket,因此服务端recv和send的第一个参数就是sServer,而客户端的第一个参数就是sClient。TCP中第一个参数都是客户端的socket,这其实也说明了UDP中客户端和服务端的概念其实没有TCP中这么明确,在本次操作中是发送方的端口就是send的第一个参数,因此在UDP中更多的是从发送端和接收端的角度去看双向通信。

  4. bufRecv[retVal] = '\0';  //每次接受到的数据因为没有初始化,初始结构都是“烫烫烫烫”,在接受到后,会把前面的若干位变成接受的数据,但是后面还是都为烫,因此需要这个语句将接受数据的缓冲收尾,接受有效的前面若干位数据,剔除烫烫烫烫。

  5. 最后通过输入0来结束本次通信,两个端口中任一一个发送了0,那就表示需要结束本次通信,两个进程会在5秒内自动关闭。

  6. 两个代码分别放于两个项目中,这里由于没有connect环节,因此并没有开始先后,两个都打开就能进行通信了。

你可能感兴趣的:(C++通信)