在连接的套接字上进行UDP响应

UDP respond on connected socket

本文关键字:UDP 响应 连接 套接字      更新时间:2023-10-16

所以我已经用TCP编程很长一段时间了,并决定选择UDP。我不太确定需要做些什么才能让我在WAN(或lan)上进行双向通信,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 "";
 }
 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制作套接字,并让合作伙伴在该端口上发送数据到客户端,第四部分是我遇到一些麻烦的地方=]

让你的sendudp函数取一个sockaddr_in。你从recvfrom得到一个,然后传递给sendto。或者,将收到的sockaddr_in传递给connect,然后使用send

我假设您发布的函数应该在客户端和服务器之间共享。为了达到这个目的,它们需要稍加修改。例如,在服务器端,recvudp应该返回客户端地址(可能作为输出参数),因为它需要稍后发送消息给它。此外,由于客户端地址结构已经填充(在服务器端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 "Includeshared.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 "";
    }
    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 "..SharedIncludeshared.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 "..SharedIncludeshared.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的数据报
收到客户端的消息:客户端你好!
收到客户端的消息:客户端你好!

客户:

Received message from server: Hello from server!
关闭套接字…

UDP是无连接协议,服务器只需要在UDP端口上开始侦听,客户端可以立即发送数据(数据报),不需要建立连接(例如与connect()/accept(),如TCP)