UDP отвечает на подключенный сокет - PullRequest
1 голос
/ 12 ноября 2011

Итак, я довольно долго программировал на TCP и решил воспользоваться 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 и отправить партнера на этот порт для передачи данных клиенту, в четвертой части у меня возникли некоторые проблемы =]

Ответы [ 2 ]

3 голосов
/ 12 ноября 2011

Заставьте вашу sendudp функцию занять sockaddr_in.Вы получаете обратно от recvfrom и можете передать его sendto.В качестве альтернативы, передайте полученный sockaddr_in на connect и затем используйте send.

2 голосов
/ 12 ноября 2011

Я предполагаю, что отправленные вами функции должны быть общими для клиента и сервера. Они должны быть немного изменены, чтобы достичь этого. Например. на стороне сервера 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
Получено сообщение от клиента: Привет от клиента!
Получено сообщение от клиента: Привет от клиента!

Клиент:

Получено сообщение от сервера: Здравствуйте, с сервера!
Закрывающая розетка ...

UDP - это протокол без установления соединения, серверу просто нужно начать прослушивать порт UDP, и клиент может немедленно отправить данные (дейтаграмму), нет необходимости устанавливать соединение (например, с connect() / accept(), как в TCP).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...