2011-11-11 31 views
1

所以我一直在用TCP編程很長一段時間,並決定選擇UDP。我不太清楚需要做什麼才能讓我能夠通過WAN進行雙向通信(或者對於LAN而言,因爲我可以打開兩個端口,所以在LAN上更容易)使用UDP一旦我將信息從客戶端發送到服務器如何才能在該套接字上響應。有沒有辦法直接連接?UDP在連接的套接字上響應

(電流速功能)

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; 
} 
string recvudp(int sock,const int size){ 
sockaddr_in SenderAddr; 
int SenderAddrSize = sizeof (SenderAddr); 
char buf[size]; 

int retsize = recvfrom(sock, buf, sizeof(buf), 0, (SOCKADDR *) & SenderAddr, &Sen derAddrSize); 
if (retsize == -1){ 
    cout << "\nRecv Error : " << WSAGetLastError(); 
    if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == 0){ 
     return ""; 
    } 
    return "\0"; 
} 
else if (retsize < size){ 
    buf[retsize] = NULL; 
} 
return buf; 
} 
int sendudp(string str, string ip, unsigned short port, int sock){ 
sockaddr_in dest; 
dest.sin_family = AF_INET; 
dest.sin_addr.s_addr = inet_addr(ip.c_str()); 
dest.sin_port = htons(port); 

int ret = sendto(sock,str.c_str(),str.size(),0, (sockaddr*)&dest,sizeof(dest)); 

if (ret == -1){ 
    cout << "\nSend Error Code : " << WSAGetLastError(); 
} 

return ret; 
} 

有了這一點,是很容易使口XXXX插座並有合作伙伴發送端口獲得的數據在客戶端上,第四部分是我」 m有一些麻煩=]

+0

UDP是無連接的,您無法連接。您將處理請求並使用sendto發送。 UDP客戶端也應該是服務器,以便接收服務器數據... – neagoegab

+2

那麼,您可以通過調用UDP套接字上的connect()來「連接」;但在TCP意義上它並不是一個真正的連接,更像是設置一個隱藏變量,告訴網絡堆棧當您調用send()時要發送數據包的IP地址。雖然sendto()可能並不值得打擾,因爲sendto()工作正常並且更容易理解。 –

回答

2

我假設你發佈的功能應該在客戶端和服務器之間共享。爲了實現這些目標,他們需要稍作修改。例如。在服務器端,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中)。

+0

完美 - 幾乎完全基於大衛的答案(Dratted多個很好的答案)實現了我的感謝深度您的答覆。 – ultifinitus

+0

我很高興你發現它有幫助:) –

3

使您的sendudp函數採取sockaddr_in。你從recvfrom得到一個,並且可以將它傳遞給sendto。或者,將接收的sockaddr_in傳遞給connect並從此開始使用send

+0

哦,我明白了,我會盡快給你一個鏡頭!謝謝! – ultifinitus

+0

這很完美。正是我需要的。謝謝。 – ultifinitus