UDP在连接的套接字上响应

时间:2011-11-11 23:19:07

标签: c++ sockets udp

所以我用TCP编程了很长一段时间,并决定选择UDP。我不太清楚需要做什么才能让我在WAN上进行双向通信(或局域网,在LAN上更容易,因为我可以打开两个端口)一旦我从客户端发送信息到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 个答案:

答案 0 :(得分:3)

sendudp功能成为sockaddr_in。您可以从recvfrom返回一个,然后将其传递给sendto。或者,将收到的sockaddr_in传递给connect,然后再使用send

答案 1 :(得分: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)。