我假設你發佈的功能應該在客戶端和服務器之間共享。爲了實現這些目標,他們需要稍作修改。例如。在服務器端,recvudp
應該返回客戶端地址(可能作爲out參數),因爲稍後需要將消息發回給它。此外,由於客戶端地址結構已經填滿(在服務器端的recvudp
或在客戶端手動),我們可以將其作爲參數傳遞給sendudp
。
我已經玩了這一點,並在Visual Studio 2010中創建了兩個簡單的項目:UDP服務器和客戶端。他們都使用上面提到的共享功能。這個代碼遠非完美,只是爲了顯示基本的UDP套接字通信。
Shared.h:
#ifndef SHARED_H
#define SHARED_H
#include <winsock2.h>
#include <string>
int udpsock(int port, const char* addr);
std::string recvudp(int sock, const int size, sockaddr_in& SenderAddr, int& SenderAddrSize);
int sendudp(std::string str, sockaddr_in dest, int sock);
#endif
Shared.cpp:
#include "Include\shared.h" // path to header - you might use different one
#include <iostream>
using namespace std;
int udpsock(int port, const char* addr)
{
int handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (handle < 1)
return -1;
sockaddr_in address;
address.sin_family = AF_INET;
if (addr == INADDR_ANY)
address.sin_addr.s_addr = INADDR_ANY;
else
address.sin_addr.s_addr = inet_addr(addr);
address.sin_port = htons((unsigned short) port);
if (bind(handle, (const sockaddr*) &address, sizeof(sockaddr_in)) < 0)
return -1;
return handle;
}
// function should return sender address info (for the code the server)
string recvudp(int sock, const int size, sockaddr_in& SenderAddr, int& SenderAddrSize)
{
// TODO: use std::vector<char> here instead of char array
char* buf = 0;
buf = new char[size];
int retsize = recvfrom(sock, buf, size, 0, (sockaddr*) &SenderAddr, &SenderAddrSize);
if(retsize == -1)
{
cout << "\nRecv Error : " << WSAGetLastError();
if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == 0)
{
return "";
}
return "\0";
}
else if (retsize < size)
{
buf[retsize] = NULL;
}
string str(buf);
delete[] buf;
return str;
}
// On the client side, prepare dest like this:
// sockaddr_in dest;
// dest.sin_family = AF_INET;
// dest.sin_addr.s_addr = inet_addr(ip.c_str());
// dest.sin_port = htons(port);
int sendudp(string str, sockaddr_in dest, int sock)
{
int ret = sendto(sock,str.c_str(),str.size(),0, (sockaddr*)&dest,sizeof(dest));
if (ret == -1)
{
cout << "\nSend Error Code : " << WSAGetLastError();
}
return ret;
}
服務器:main.cpp中:
#include <winsock2.h>
#include <string.h>
#include <iostream>
#include "..\Shared\Include\shared.h"
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
#define SERVER_PORT 27015
#define MAX_MSG 1024
using namespace std;
int main(int argc, char *argv[])
{
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if(nResult != NO_ERROR)
{
cout << "WSAStartup failed with error: " << nResult << endl;
return 1;
}
sock = udpsock(SERVER_PORT, "127.0.0.1");
cout << "Waiting for datagram on port: " << SERVER_PORT << endl;
while(1)
{
sockaddr_in clientAddr;
// receive message
int clientAddrLen = sizeof(clientAddr);
cout << "Received message from the client: " << recvudp(sock, MAX_MSG, clientAddr, clientAddrLen) << endl;
sendudp("Hello from server!", clientAddr, sock);
}
WSACleanup();
return 0;
}
客戶:main.cpp中:
#include <winsock2.h>
#include <iostream>
#include "..\Shared\Include\shared.h"
using namespace std;
#define MAX_MSG 1024
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
int main(int argc, char* argv[])
{
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (nResult != NO_ERROR)
{
cout << "WSAStartup failed with error: " << nResult << endl;
return 1;
}
SOCKET sock = INVALID_SOCKET;
// Create a socket for sending data - it does not need to be binded like listening socket!
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sock == INVALID_SOCKET)
{
cout << socket failed with error: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
unsigned short Port = 27015;
sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = inet_addr("127.0.0.1");
dest.sin_port = htons(Port);
sendudp("Hello from client!", dest, sock);
sockaddr_in RecvAddr;
int recvaddrlen = sizeof(RecvAddr);
cout << "Received message from the server: " << recvudp(sock, MAX_MSG, RecvAddr, recvaddrlen) << endl;
cout << "Closing socket..." << endl;
nResult = closesocket(sock);
if(nResult == SOCKET_ERROR)
{
cout << "closesocket failed with error: " << WSAGetLastError() << endl;
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
如果運行客戶端兩次輸出爲:
服務器:從客戶27015
收到的消息:
等待數據包在端口你好從客戶端!
從客戶端收到消息:來自客戶端的Hello!
客戶端:
從服務器收到的消息:服務器問候!
關閉插槽...
UDP是無連接的協議,服務器只需要啓動監聽UDP端口和客戶端上可以發送數據(數據報)馬上,就沒有必要建立連接(例如,用connect()
/accept()
,像在TCP中)。
UDP是無連接的,您無法連接。您將處理請求並使用sendto發送。 UDP客戶端也應該是服務器,以便接收服務器數據... – neagoegab
那麼,您可以通過調用UDP套接字上的connect()來「連接」;但在TCP意義上它並不是一個真正的連接,更像是設置一個隱藏變量,告訴網絡堆棧當您調用send()時要發送數據包的IP地址。雖然sendto()可能並不值得打擾,因爲sendto()工作正常並且更容易理解。 –