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

运行环境:VS2015

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

UDP通信流程

【来自https://blog.csdn.net/u012801153/article/details/79363575

服务器端步骤:

  1. 加载套接字库,创建套接字(WSAStartup()/socket());
  2. 绑定套接字到一个IP地址和一个端口上(bind());
  3. 与客户端进行通信,接收来自client的信息(recvfrom()),发送回复信息至client(sendto());
  4. 关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

客户端步骤:

  1. 加载套接字库,创建套接字(WSAStartup()/socket());
  2. 绑定套接字到一个IP地址和一个端口上(bind());
  3. 和服务器端进行通信,发送请求信息至server(sendto());接收server的回复信息(recvfrom());
  4. 关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。

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

服务端代码Server.cpp:

#include "winsock2.h"
#include <WS2tcpip.h>
#include <iostream>

#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 Param
	SOCKET			sServer;		    //Server Socket 
	SOCKADDR_IN		servAddr;		    //Server Addr 
	SOCKADDR_IN     clientAddr;         //Client Addr
	SOCKADDR_IN     send_Data_Addr;     //the Addr that send the data(in this demo, send_Data_Addr = clientAddr)
	int				nAddrLen_send = sizeof(send_Data_Addr);
	char			bufSend[BUF_SIZE];	//send buffer
	char			bufRecv[BUF_SIZE];  //receive buffer
	int				retVal;			    //return value
	char*			closeSymbol = "0";  //symbol of close
	char*           bufReq = "Request Reply"; // symbol of Request Reply
	char            str[INET_ADDRSTRLEN];
	
	// Server Addr
	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)5000);

	// Client Addr
	clientAddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", (void*)&clientAddr.sin_addr.S_un.S_addr);
	clientAddr.sin_port = htons((short)4999);

	// Initialize the socket of dll
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup failed !" << endl;
		return 1;
	}

	// Create server socket 
	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;
	}

	// Bind the socket with IP and Port 
	retVal = bind(sServer, (LPSOCKADDR)&servAddr, sizeof(SOCKADDR_IN));
	if (SOCKET_ERROR == retVal)
	{
		cout << "bind failed!" << endl;
		closesocket(sServer);	 
		WSACleanup();			 
		return -1;
	}
	else
	{
		cout << "Server UDP Socket bind IP & Port !" << endl;
		cout << "Server Address = " << inet_ntop(AF_INET, &servAddr.sin_addr, str, sizeof(str)) << ":" << ntohs(servAddr.sin_port) << endl;
	}

	// loop - receive the request from the client socket / send the reply to the client socket
	while (true) {
		// Initialize the buffer
		ZeroMemory(bufRecv, BUF_SIZE);

		// Server socket receive request from send_Data_Addr(clientAddr) 
		retVal = recvfrom(sServer, bufRecv, BUF_SIZE, 0, (sockaddr *)&send_Data_Addr, &nAddrLen_send);
		if (SOCKET_ERROR == retVal)
		{
			cout << "Recv Failed!" << endl;
			closesocket(sServer);
			WSACleanup();
			Sleep(5000);
			return -1;
		}
		
		// Check the data from client 
		bufRecv[retVal] = '\0';			// Set the last bit as \0 to avoid the wrong data  
		cout << "Data recv from Client Socket[" << inet_ntop(AF_INET, &send_Data_Addr.sin_addr, str, sizeof(str)) << ":" << ntohs(send_Data_Addr.sin_port) << "] : " << bufRecv << endl;
		// When the data from another socket is ‘0’,exit the loop and finish UDP Communication
		if (!strcmp(bufRecv, closeSymbol))
		{
			cout << "Client UDP Socket wants to finish this communication" << endl;
			closesocket(sServer);
			WSACleanup();
			Sleep(5000);
			break;
		}
		else
		{
			// Automatically send the request reply to client  
			sendto(sServer, bufReq, strlen(bufReq), 0, (sockaddr *)&send_Data_Addr, nAddrLen_send);
		}		
	}
 
	return 0;
}

客户端代码Client.cpp:

#include "winsock2.h"
#include <WS2tcpip.h>
#include <iostream>

#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 Param
	SOCKET			sClient;		    //Server Socket 
	SOCKADDR_IN		servAddr;		    //Server Addr 
	SOCKADDR_IN     clientAddr;         //Client Addr
	SOCKADDR_IN     send_Data_Addr;     //the Addr that send the data(in this demo, send_Data_Addr = servAddr)
	int				nAddrLen_server = sizeof(servAddr);
	int				nAddrLen_send = sizeof(send_Data_Addr);
	char			bufSend[BUF_SIZE];	//send buffer
	char			bufRecv[BUF_SIZE];  //receive buffer
	int				retVal;			    //return value
	char*			closeSymbol = "0";  //symbol of close
	char            str[INET_ADDRSTRLEN];

	// Server Addr
	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)5000);

	// Client Addr
	clientAddr.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1", (void*)&clientAddr.sin_addr.S_un.S_addr);
	clientAddr.sin_port = htons((short)4999);

	// Initialize the socket of dll
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		cout << "WSAStartup failed !" << endl;
		return 1;
	}

	// Create client socket 
	sClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == sClient)
	{
		cout << "socket failed!" << endl;
		WSACleanup();
		Sleep(5000);
		return  -1;
	}
	else
	{
		cout << "Client UDP Socket init!" << endl;
	}

	// Bind the client socket with IP and Port 
	retVal = bind(sClient, (LPSOCKADDR)&clientAddr, sizeof(SOCKADDR_IN));
	if (SOCKET_ERROR == retVal)

	{
		cout << "bind failed!" << endl;
		closesocket(sClient);
		WSACleanup();
		Sleep(5000);
		return -1;
	}
	else
	{
		cout << "Client UDP Socket bind IP & Port !" << endl;
		cout << "Client Address = " << inet_ntop(AF_INET, &clientAddr.sin_addr, str, sizeof(str)) << ":" << ntohs(clientAddr.sin_port) << endl;
	}

	// loop - client socket receive data
	while (true) {
		// Initialize the buffer
		ZeroMemory(bufRecv, BUF_SIZE);

		// Send the request to server
		cout << "Data send to   Server Socket [" << inet_ntop(AF_INET, &servAddr.sin_addr, str, sizeof(str)) << ":" << ntohs(servAddr.sin_port) << "] : ";
		cin >> bufSend;
		sendto(sClient, bufSend, strlen(bufSend), 0, (sockaddr *)&servAddr, nAddrLen_server);

		// When the data from another socket is ‘0’,exit the loop and finish TCP Communication	
		if (!strcmp(bufSend, closeSymbol))
		{
			cout << "Client Socket wants to finish this communication" << endl;
			closesocket(sClient);
			WSACleanup();
			Sleep(5000);
			break;
		}

		// Client socket receive reply from send_Data_Addr(servAddr) 
		retVal = recvfrom(sClient, bufRecv, BUF_SIZE, 0, (sockaddr *)&send_Data_Addr, &nAddrLen_send);
		if (SOCKET_ERROR == retVal)
		{
			cout << "Recv Failed!" << endl;
			closesocket(sClient);
			WSACleanup();
			Sleep(5000);
			return -1;
		}

		// check the data
		bufRecv[retVal] = '\0';			// Set the last bit as \0 to avoid the wrong data
		cout << "Data recv from Server Socket [" << inet_ntop(AF_INET, &send_Data_Addr.sin_addr, str, sizeof(str)) << ":" << ntohs(send_Data_Addr.sin_port) << "] : " << bufRecv << endl;
	}
	return 0;
}

注意事项

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

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

  3. 在UDP协议中,recvfrom和sendto的第一个参数的逻辑和TCP中有些区别。

      recvfrom第一个参数是当前接收端的socket,第五个参数表示当前发送端的socket地址;

      sendto第一个参数是当前发送端的socket,第五个参数表示当前接收端的socket地址。

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

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

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

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值