C ++ UDP双向客户端/服务器交付

时间:2014-02-19 21:41:59

标签: c++ sockets networking udp

我正在构建一个UDP服务器来处理飞行模拟器中的位置更新(因此,数据包将是快速的,并且保证不需要传送)。服务器根据服务器中的“会话”创建并绑定UDP套接字。然后,服务器将位置信息从主设备传送给观众。这个逻辑工作(虽然,我目前已将其编码为在局域网上回送发送给发送方的每条消息进行测试)(我在同一系统上设置了服务器和客户端进行测试)。

当我将其移至WAN时,客户端无法再从服务器接收消息,但服务器从客户端收到消息。我担心这是一个防火墙/端口阻塞问题,但是,我来到这里是为了找到一种不同的/正确的方式来做我正在做的事情,或指示我如何逃脱阻止。

服务器的代码如下:

user newuser;
ommand = raw_connections[i].sockfd;     
sockaddr_in sin;
socklen_t len = sizeof(sin);
getsockname(raw_connections[i].sockfd, (sockaddr*)&sin, &len); //raw_connections is a vector of sockets that haven't yet authenticated
newuser.addr.sin_addr = sin.sin_addr;
newuser.addr.sin_family = sin.sin_family;
newuser.addr.sin_port = atoi(inp[4].c_str()); //the UDP port as specified by the client
flights[fpos].users.push_back(newuser); //fpos is the index in the vector

上述数据是由我程序的UI和系统之间的TCP套接字调用的(其中一部分将处理用户列表,管理等等) - UDP端口是从位置系统给出的,按如下方式生成:)

sockaddr_in service1;
service1.sin_family = AF_INET;
service1.sin_addr.s_addr = INADDR_ANY;
service1.sin_port = htons(0);

server_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

mode = 1;
ioctlsocket(server_sock, FIONBIO, &mode);

soreuse = 1;
ioctlsocket(server_sock, SO_REUSEADDR, &soreuse);


sockaddr_in sin;
socklen_t len = sizeof(sin);
getsockname(server_sock, (sockaddr*)&sin, &len);
udpport = sin.sin_port; //the value of UDP port here is what's given to the client, and ultimately, the server

服务器的发送部分是:

sockaddr_in addr;
int sz = sizeof(addr);
char* buff = new char[1024];            
int recvdf = recvfrom(gp->data_socket,buff,1024,NULL,(sockaddr*)&addr,&sz);
if (recvdf > 0) {
    cout << "got packet with size " << recvdf  << " on port " << addr.sin_port << endl;                             
    unsigned sz = gp->users.size();
    for (unsigned i = 0; i < sz; i++) {
        sendto(gp->data_socket, buff, recvdf, NULL, (sockaddr*)&gp->users[i].addr, sizeof(gp->users[i].addr));
    }
}
delete[] buff;
Sleep(1);

最后,客户端的互动是这样的:

char* buffer = new char[1024];
int recvd = recvfrom(server_sock, buffer, 1024, NULL, (sockaddr*)&server_addr, &sockaddrsize);
if (recvd > 0) {
    int i = 282;
}
delete[] buffer;
sendto(server_sock, "test\0", 5, NULL, (sockaddr*)&server_addr, sockaddrsize);

有任何建议让它运作? (声明的无用变量在Visual Studio中用作断点。)

2 个答案:

答案 0 :(得分:1)

您的客户端很可能受到阻止来自Internet的传入UDP数据包的防火墙的保护。为了解决这个问题,您可以指示客户端的系统管理员在防火墙上为您的服务器发送数据包的端口上的传入UDP手动打开一个漏洞(如果涉及NAT,则需要设置端口 - 也可以在NAT上转发),或者您可以调查UDP hole-punching techniques

上述两种技术都不是特别优雅或强大,因此您可能只想为您的软件设置一个后备模式,其中所有数据都通过TCP流传送。性能不会那么好,但有时候让它“正常工作”会更好,TCP也会允许这样做。

答案 1 :(得分:0)

首先,你的措辞非常混乱:“UDP服务器”“查看器”“客户端”“系统”“位置系统”......您写道:“我目前已对其进行编码,以便将发送给发送者的每条消息发送回发送者,以进行测试。”但是,您的“客户端”代码仅回复固定字符串"test\0"

如果您有任何疑问,您应该编写一个与主项目分开的最小测试代码。

我认为“观众”是“客户”。

你写了

  

服务器的发送部分是:

int recvdf = recvfrom(gp->data_socket, ... );
for (unsigned i = 0; i < sz; i++) {
    sendto(gp->data_socket, ..., (sockaddr*)&gp->users[i].addr ... );
}
  

最后,客户端的互动是这样的:

int recvd = recvfrom(server_sock, ... );
sendto(server_sock, "test\0", ... (sockaddr*)&server_addr, ... );

但你也回复了@Jeremy

  

我很害怕,但传统上防火墙不允许在它发送的端口上接收数据吗?

这两者之间存在矛盾。您的服务器首先将UDP数据报发送到客户端,然后客户端响应。服务器和客户端之间的第一个数据包是从服务器发送的。这就是我从你的代码中理解的。您已经知道这不适用于典型的防火墙。

虽然@Jeremy的正确之处在于每个防火墙的配置都不同,但大多数(家用)防火墙都能满足您的需求。我怀疑你的代码没有利用常见防火墙的这种典型行为。例如,如果您的“客户端”端可以使用Windows Internet时间同步(实际上是UDP SNTP客户端)并将UDP数据报从短暂的源端口发送到时间服务器,您应该能够从服务器接收UDP数据报。好。