运行环境:VS2015
TCP双向通信的代码及介绍可以见 C++ socket编程 实现服务端与客户端的双向TCP通讯
UDP通信流程
【来自https://blog.csdn.net/u012801153/article/details/79363575】
服务器端步骤:
- 加载套接字库,创建套接字(WSAStartup()/socket());
- 绑定套接字到一个IP地址和一个端口上(bind());
- 与客户端进行通信,接收来自client的信息(recvfrom()),发送回复信息至client(sendto());
- 关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。
客户端步骤:
- 加载套接字库,创建套接字(WSAStartup()/socket());
- 绑定套接字到一个IP地址和一个端口上(bind());
- 和服务器端进行通信,发送请求信息至server(sendto());接收server的回复信息(recvfrom());
- 关闭套接字,关闭加载的套接字库(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;
}
注意事项
-
大体代码和TCP方式是相同的。但是UDP因为追求通信的实时性,减少了许多环节。
-
sClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 这里需要采用SOCK_DGRAM数据报方式,协议选择UDP。
-
在UDP协议中,recvfrom和sendto的第一个参数的逻辑和TCP中有些区别。
recvfrom第一个参数是当前接收端的socket,第五个参数表示当前发送端的socket地址;
sendto第一个参数是当前发送端的socket,第五个参数表示当前接收端的socket地址。
-
bufRecv[retVal] = '\0'; //每次接受到的数据因为没有初始化,初始结构都是“烫烫烫烫”,在接受到后,会把前面的若干位变成接受的数据,但是后面还是都为烫,因此需要这个语句将接受数据的缓冲收尾,接受有效的前面若干位数据,剔除烫烫烫烫。
-
最后通过输入0来结束本次通信,若客户端发送了一个"0",那就表示需要结束本次通信,两个进程会在5秒内自动关闭。
-
两个代码分别放于两个项目中,这里由于没有connect环节,因此并没有开始先后,两个都打开就能进行通信了。