Library like ENet, but for TCP? - c++

I'm not looking to use boost::asio, it is overly complex for my needs.
I'm building a game that is cross platform, for desktop, iPhone and Android.
I found a library called ENet which is pretty much what I need, but it uses UDP which does not seem to support encryption and a few other things. Given that the game is an event driven card game, TCP seems like the right fit.
However, all I have found is WINSOCK / berkley sockets and bost::asio.
Here is a sample client server application with ENet:
#include <enet/enet.h>
#include <stdlib.h>
#include <string>
#include <iostream>
class Host
{
ENetAddress address;
ENetHost * server;
ENetHost* client;
ENetEvent event;
public:
Host()
:server(NULL)
{
enet_initialize();
setupServer();
}
void setupServer()
{
if(server)
{
enet_host_destroy(server);
server = NULL;
}
address.host = ENET_HOST_ANY;
/* Bind the server to port 1234. */
address.port = 1721;
server = enet_host_create (& address /* the address to bind the server host to */,
32 /* allow up to 32 clients and/or outgoing connections */,
2 /* allow up to 2 channels to be used, 0 and 1 */,
0 /* assume any amount of incoming bandwidth */,
0 /* assume any amount of outgoing bandwidth */);
}
void daLoop()
{
while(true)
{
/* Wait up to 1000 milliseconds for an event. */
while (enet_host_service (server, & event, 5000) > 0)
{
ENetPacket * packet;
switch (event.type)
{
case ENET_EVENT_TYPE_CONNECT:
printf ("A new client connected from %x:%u.\n",
event.peer -> address.host,
event.peer -> address.port);
/* Store any relevant client information here. */
event.peer -> data = "Client information";
/* Create a reliable packet of size 7 containing "packet\0" */
packet = enet_packet_create ("packet",
strlen ("packet") + 1,
ENET_PACKET_FLAG_RELIABLE);
/* Extend the packet so and append the string "foo", so it now */
/* contains "packetfoo\0" */
enet_packet_resize (packet, strlen ("packetfoo") + 1);
strcpy ((char*)& packet -> data [strlen ("packet")], "foo");
/* Send the packet to the peer over channel id 0. */
/* One could also broadcast the packet by */
/* enet_host_broadcast (host, 0, packet); */
enet_peer_send (event.peer, 0, packet);
/* One could just use enet_host_service() instead. */
enet_host_flush (server);
break;
case ENET_EVENT_TYPE_RECEIVE:
printf ("A packet of length %u containing %s was received from %s on channel %u.\n",
event.packet -> dataLength,
event.packet -> data,
event.peer -> data,
event.channelID);
/* Clean up the packet now that we're done using it. */
enet_packet_destroy (event.packet);
break;
case ENET_EVENT_TYPE_DISCONNECT:
printf ("%s disconected.\n", event.peer -> data);
/* Reset the peer's client information. */
event.peer -> data = NULL;
}
}
}
}
~Host()
{
if(server)
{
enet_host_destroy(server);
server = NULL;
}
atexit (enet_deinitialize);
}
};
class Client
{
ENetAddress address;
ENetEvent event;
ENetPeer *peer;
ENetHost* client;
public:
Client()
:peer(NULL)
{
enet_initialize();
setupPeer();
}
void setupPeer()
{
client = enet_host_create (NULL /* create a client host */,
1 /* only allow 1 outgoing connection */,
2 /* allow up 2 channels to be used, 0 and 1 */,
57600 / 8 /* 56K modem with 56 Kbps downstream bandwidth */,
14400 / 8 /* 56K modem with 14 Kbps upstream bandwidth */);
if (client == NULL)
{
fprintf (stderr,
"An error occurred while trying to create an ENet client host.\n");
exit (EXIT_FAILURE);
}
/* Connect to some.server.net:1234. */
enet_address_set_host (& address, "192.168.2.13");
address.port = 1721;
/* Initiate the connection, allocating the two channels 0 and 1. */
peer = enet_host_connect (client, & address, 2, 0);
if (peer == NULL)
{
fprintf (stderr,
"No available peers for initiating an ENet connection.\n");
exit (EXIT_FAILURE);
}
/* Wait up to 5 seconds for the connection attempt to succeed. */
if (enet_host_service (client, & event, 20000) > 0 &&
event.type == ENET_EVENT_TYPE_CONNECT)
{
std::cout << "Connection to some.server.net:1234 succeeded." << std::endl;
}
else
{
/* Either the 5 seconds are up or a disconnect event was */
/* received. Reset the peer in the event the 5 seconds */
/* had run out without any significant event. */
enet_peer_reset (peer);
puts ("Connection to some.server.net:1234 failed.");
}
}
void daLoop()
{
ENetPacket* packet;
/* Create a reliable packet of size 7 containing "packet\0" */
packet = enet_packet_create ("backet",
strlen ("backet") + 1,
ENET_PACKET_FLAG_RELIABLE);
/* Extend the packet so and append the string "foo", so it now */
/* contains "packetfoo\0" */
enet_packet_resize (packet, strlen ("backetfoo") + 1);
strcpy ((char*)& packet -> data [strlen ("backet")], "foo");
/* Send the packet to the peer over channel id 0. */
/* One could also broadcast the packet by */
/* enet_host_broadcast (host, 0, packet); */
enet_peer_send (event.peer, 0, packet);
/* One could just use enet_host_service() instead. */
enet_host_flush (client);
while(true)
{
/* Wait up to 1000 milliseconds for an event. */
while (enet_host_service (client, & event, 1000) > 0)
{
ENetPacket * packet;
switch (event.type)
{
case ENET_EVENT_TYPE_RECEIVE:
printf ("A packet of length %u containing %s was received from %s on channel %u.\n",
event.packet -> dataLength,
event.packet -> data,
event.peer -> data,
event.channelID);
/* Clean up the packet now that we're done using it. */
enet_packet_destroy (event.packet);
break;
}
}
}
}
~Client()
{
atexit (enet_deinitialize);
}
};
int main()
{
std::string a;
std::cin >> a;
if(a == "host")
{
Host host;
host.daLoop();
}
else
{
Client c;
c.daLoop();
}
return 0;
}
I looked at some socket tutorials and they seemed a bit too low level.
I just need something that abstracts away the platform (eg, no WINSOCKS) and that has basic ability to keep track of connected clients and send them messages.
Thanks

A late reply but maybe it will help someone else.
Encryption is a high level feature. TCP nor UDP alone does not support encryption by itself. They are both low level protocols. Actually, we could say that TCP is a higher level protocol compared to UDP, because TCP includes some advanced features which might be useful ... or not, depending on a project where it will be used.
ENet is good because it provides the benefits of both worlds - the reliability of TCP and the freedom and light weight of UDP. There is nothing wrong with the fact that ENet internally uses only UDP and not TCP, because ENet is able to provide almost everything that TCP does, and much more.
If you wish to have encryption, you will have to add it yourself, no matter if you chose TCP, UDP or ENet. So I suggest you to continue using ENet if you feel comfortable with it, and when you get your game up and running, you can add any encryption algorithm you wish, as long as you choose the same algorithm for both client and server. You can pick a 3rd party encryption library and just pass your network packets through it. ENet doesn't care, what data is being sent through it.
Maybe the following article will help with ENet:
Network Programming with ENet By Mike Diehl
and also take a look at the following Stackoverflow topic about encryption for small network packets:
Best practices for encrypting continuous/small UDP data

I heard Mordor have an SSL layer which can be used for network encryption.

clsocket is a great little TCP library, permissive open source license, Windows, Mac OSX, Linux:
https://github.com/DFHack/clsocket

Related

ZMQ_HEARTBEAT_TTL does not discard outgoing queue even if ZMQ_LINGER is set

I have a server which uses a ZMQ_ROUTER to communicate with ZMQ_DEALER clients. I set the ZMQ_HEARTBEAT_IVL and ZMQ_HEARTBEAT_TTL options on the client socket to make the client and server ping pong each other. Beside, because of the ZMQ_HEARTBEAT_TTL option, the server will timeout the connection if it does not receive any pings from the client in a time period, according to zmq man page:
The ZMQ_HEARTBEAT_TTL option shall set the timeout on the remote peer for ZMTP heartbeats. If
this option is greater than 0, the remote side shall time out the connection if it does not
receive any more traffic within the TTL period. This option does not have any effect if
ZMQ_HEARTBEAT_IVL is not set or is 0. Internally, this value is rounded down to the nearest
decisecond, any value less than 100 will have no effect.
Therefore, what I expect the server to behave is that, when it does not receive any traffic from a client in a time period, it will close the connection to that client and discard all the messages in the outgoing queue after the linger time expires. I create a toy example to check if my hypothesis is correct and it turns out that it is not. The chain of events is as followed:
The server sends a bunch of data to the client.
The client receives and processes the data, which is slow.
All send commands return successfully.
While the client is still receiving the data, I unplug the internet cable.
After a few seconds (set by the ZMQ_HEARTBEAT_TTL option), the server starts sending FIN signals to the client, which are not being ACKed back.
The outgoing messages are not discarded (I check the memory consumption) even after a while. They are discarded only if I call zmq_close on the router socket.
So my question is, is this suppose to be how one should use the ZMQ heartbeat mechanism? If it is not then is there any solution for what I want to achieve? I figure that I can do heartbeat myself instead of using ZMQ's built in. However, even if I do, it seems that ZMQ does not provide a way to close a connection between a ZMQ_ROUTER and a ZMQ_DEALER, although that another version of ZMQ_ROUTER - ZMQ_STREAM provides a way to do this by sending an identity frame followed by an empty frame.
The toy example is below, any help would be thankful.
Server's side:
#include <zmq.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
void *context = zmq_ctx_new();
void *router = zmq_socket(context, ZMQ_ROUTER);
int router_mandatory = 1;
zmq_setsockopt(router, ZMQ_ROUTER_MANDATORY, &router_mandatory, sizeof(router_mandatory));
int hwm = 0;
zmq_setsockopt(router, ZMQ_SNDHWM, &hwm, sizeof(hwm));
int linger = 3000;
zmq_setsockopt(router, ZMQ_LINGER, &linger, sizeof(linger));
char bind_addr[1024];
sprintf(bind_addr, "tcp://%s:%s", argv[1], argv[2]);
if (zmq_bind(router, bind_addr) == -1) {
perror("ERROR");
exit(1);
}
// Receive client identity (only 1)
zmq_msg_t identity;
zmq_msg_init(&identity);
zmq_msg_recv(&identity, router, 0);
zmq_msg_t dump;
zmq_msg_init(&dump);
zmq_msg_recv(&dump, router, 0);
printf("%s\n", (char *) zmq_msg_data(&dump)); // hello
zmq_msg_close(&dump);
char buff[1 << 16];
for (int i = 0; i < 50000; ++i) {
if (zmq_send(router, zmq_msg_data(&identity),
zmq_msg_size(&identity),
ZMQ_SNDMORE) == -1) {
perror("ERROR");
exit(1);
}
if (zmq_send(router, buff, 1 << 16, 0) == -1) {
perror("ERROR");
exit(1);
}
}
printf("OK IM DONE SENDING\n");
// All send commands have returned successfully
// While the client is still receiving data, I unplug the intenet cable on the client machine
// After a while, the server starts sending FIN signals
printf("SLEEP before closing\n"); // At this point, the messages are not discarded (memory usage is high).
getchar();
zmq_close(router);
zmq_ctx_destroy(context);
}
Client's side:
#include <zmq.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
void *context = zmq_ctx_new();
void *dealer = zmq_socket(context, ZMQ_DEALER);
int heartbeat_ivl = 3000;
int heartbeat_timeout = 6000;
zmq_setsockopt(dealer, ZMQ_HEARTBEAT_IVL, &heartbeat_ivl, sizeof(heartbeat_ivl));
zmq_setsockopt(dealer, ZMQ_HEARTBEAT_TIMEOUT, &heartbeat_timeout, sizeof(heartbeat_timeout));
zmq_setsockopt(dealer, ZMQ_HEARTBEAT_TTL, &heartbeat_timeout, sizeof(heartbeat_timeout));
int hwm = 0;
zmq_setsockopt(dealer, ZMQ_RCVHWM, &hwm, sizeof(hwm));
char connect_addr[1024];
sprintf(connect_addr, "tcp://%s:%s", argv[1], argv[2]);
zmq_connect(dealer, connect_addr);
zmq_send(dealer, "hello", 6, 0);
size_t size = 0;
int i = 0;
while (size < (1ll << 16) * 50000) {
zmq_msg_t msg;
zmq_msg_init(&msg);
if (zmq_msg_recv(&msg, dealer, 0) == -1) {
perror("ERROR");
exit(1);
}
size += zmq_msg_size(&msg);
printf("i = %d, size = %ld, total = %ld\n", i, zmq_msg_size(&msg), size); // This causes the cliet to be slow
// Somewhere in this loop I unplug the internet cable.
// The client starts sending FIN signals as well as trying to reconnect. The recv command hangs forever.
zmq_msg_close(&msg);
++i;
}
zmq_close(dealer);
zmq_ctx_destroy(context);
}
PS: I know that setting the highwater mark to unlimited is bad practice, however I figure that the problem will be the same even if the highwater mark is low so let's ignore it for now.

Long delay between a write and a read using UDP

I have a Raspberry Pi 4 running a C++ program where it receives and sends data via UDP. The RPi is setup as a UDP server.
The code for UDP.hpp is:
#pragma once
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string>
using namespace std;
/////GLOBAL CONSTANTS/////
const int c_PORT = 8080;
class UDP
{
private:
int fdSocketUDP_; //File descriptor for UDP socket
int ClientAddressLength_; //Length of client address
struct sockaddr_in ServerAddress_; //Struct handling internet address for server
struct sockaddr_in ClientAddress_; //Struct handling internet address for client
public:
UDP(); //Initialize and bind socket
~UDP(); //Close socket
string readUDP(const int readSize); //Read via UDP protocol
void writeUDP(string message); //Write via UDP protocol
};
The code for UDP.cpp is:
#include "udp.hpp"
UDP::UDP()
{
if ((fdSocketUDP_ = socket(AF_INET, SOCK_DGRAM, 0)) < 0) //Create UDP socket
{
perror("Error - socket creation - udp.cpp");
exit(EXIT_FAILURE);
}
memset(&ServerAddress_, 0, sizeof(ServerAddress_)); //Sets ServerAddress_ to 0
memset(&ClientAddress_, 0, sizeof(ClientAddress_)); //Sets ClientAddress_ to 0
ServerAddress_.sin_family = AF_INET; //Address family, must be AF_INET = IPv4
ServerAddress_.sin_port = htons(c_PORT); //PORT number, convert PORT number to network byte order using htons()
ServerAddress_.sin_addr.s_addr = INADDR_ANY; //IP-Address of host (server IP), INADDR_ANY gets this IP Address
if (bind(fdSocketUDP_, (const struct sockaddr *)&ServerAddress_, sizeof(ServerAddress_)) < 0) //Bind the socket to ServerAddress_
{
perror("Error - socket bind - udp.cpp");
exit(EXIT_FAILURE);
}
}
UDP::~UDP()
{
close(fdSocketUDP_); //Close socket
}
string UDP::readUDP(const int readSize)
{
char readMsg[readSize] = {0}; //Read buffer
ClientAddressLength_ = sizeof(ClientAddress_);
if ((recvfrom(fdSocketUDP_, readMsg, readSize, 0, (struct sockaddr *)&ClientAddress_, (socklen_t *)&ClientAddressLength_)) < 0) //Receive data via UDP protocol
{
perror("Error - recvfrom - udp.cpp");
exit(EXIT_FAILURE);
}
string str(readMsg); //Convert char array to string
str = str.substr(0, readSize); //Make sure the string is the length of readsize
return str;
}
void UDP::writeUDP(string message)
{
//Make char array
int writeSize = message.size();
char writeMsg[writeSize + 1] = {'\0'};
//Convert string message to char array
for (int i = 0; i < writeSize; i++)
{
writeMsg[i] = message[i];
}
if ((sendto(fdSocketUDP_, writeMsg, writeSize, 0, (const struct sockaddr *)&ClientAddress_, (socklen_t)ClientAddressLength_)) < 0) //Send data via UDP protocol
{
perror("Error - sendto - udp.cpp");
exit(EXIT_FAILURE);
}
}
I then have a windows 10 laptop, running a Labview program that also receives and sends data via UDP. The laptop is setup as a UDP client. Below are som examples of the UDP setup in Labview.
Image 1 (Open UDP connection):
Image 2 (Close UDP connection):
Image 3 (Write and read UDP in Labview):
Above, the Labview program on the laptop sends 3 ("103") + 37 (not shown) bytes of data to the RPi, and then receives 16 bytes of data from the RPi.
The laptop and RPi are connected via a LAN-cable on a local network. The RPi uses IP-address 10.10.10.10 and port 8080, and the laptop uses IP-address 10.10.10.1 and port 1000.
Below are a Wireshark measurement, that measures the time between the different send and receive commands between the RPi and laptop.
Image 4 (wireshark measurement):
The "Len=3" is used by the RPi to determine what function to run in the C++ code. The "Len=52" and "Len=37" is data sent from the laptop (Labview) to the RPi (C++ code). The "Len=16" is data sent from the RPi to the laptop.
The laptop first sends 3+52 bytes of data to the RPi (client sends data to server). The laptop then sends 3+37 bytes of data to the RPi (client sends data to server). The RPi then sends 16 bytes of data back to the laptop (server sends data to client)... and so on.
One command (3+52 bytes or 3+37+16 bytes) takes about ~8ms to finish, with ~2ms latency (on average) between each command. As you can see, the data sizes between the RPi and laptop are "relatively" small (3/37/52 bytes).
Now my problem: Sometimes there is a delay of ~20ms between the commands (10 times longer than the average of ~2ms), and I don't know why... (this is shown with the red dots on image 4). This delay often comes after the RPi (UDP server) sends data to the laptop (UDP client - the 16 bytes of data), but it can happen at different places, as shown on image 4 (after the laptop sends 52 bytes to the RPi). I think it has something to do with the UDP, maybe the setup, maybe it has something to do with ARP, but I don't know. I have tried overclocking the RPi, tweaking the priority of the C++ program on the RPi, tweaking the C++ code, but that doesn't seem to be the bottleneck.
It's like the UDP connection between the laptop and RPi is "lost" or "paused" sometimes and it then takes some time for the connection to come back on track.
I found a solution to my problem. To solve the long delay, i had to lower the UDP read buffer, since i'm only sending small packages via UDP.
To do this, i formatted the sysctl.conf file on the RPi, located in the /etc folder.
I added the lines:
net.core.rmem_default = 4096
net.core.rmem_max = 4096

Sockets: my select() function logic has undefined behavior

I implemented a program that receives from one socket and sends/receives from the other socket.
For this i use polling of select(), in socket 1, i receive data at a high data rate, while in the other socket i receive periodic message and requests to receive data from the first socket.
When there is no request "from socket 2" to delegate the data from socket 1 to socket2 , i receive data from socket 1 normal and with no problem. However, say i received two requests "socket 2" while data is being received in socket 1, the second request breaks the the data reception as if it could no longer keep up with rate "rate isn't high really is only 150 Hz".
The pseudo code i do in the main():
fd_set readfds, rd_fds, writefds, wr_fds;
struct timeval tv;
do
{
do
{
rd_fds = readfds;
wr_fds = writefds;
FD_ZERO (&rd_fds);
FD_SET (sock1, &rd_fds);
FD_SET (sock2, &rd_fds);
FD_SET (sock1, &wr_fds);
tv.tv_sec = 0;
tv.tv_usec = 20;
int ls = sock2 + 1;
rslt = select (ls, &rd_fds, &wr_fds, NULL, &tv);
}
while (rslt == -1 && errno == EINTR);
if (FD_ISSET (sock1, &rd_fds))
{
rs1 = recvfrom (sock1, buff, size of the buff, ....);
if (rs1 > 0)
{
if (rs1 = alive message)
{
/* system is alive; */
}
else if (rs1 == request message)
{
/* store Request info (list or vector) */
}
else {}
}
}
if (FD_ISSET (StructArg.sock2, &rd_fds))
{
rs2 = recv (sock2, ..., 0);
if (rs2 > 0)
{
if ( /* Message (high rate) is from sock 2 */ )
{
/* process this message and do some computation */
int sp1 = sendto (sock1, .....);
if (sp1 < 0)
{
perror ("Failed data transmission ");
}
else
{
/* increase some counters */
}
}
}
}
if (FD_ISSET (sock1, &wr_fds))
{
/*
if there info stored in the list
do some calculaitons then send to sock 1
*/
if (sendto (sock1, ... ...) < 0)
{
perror ("Failed data transmission");
}
else
{
/* increase counter */
}
}
FD_CLR (sock1, &rd_fds);
FD_CLR (sock2, &rd_fds);
}
while (1);
Again, the question is, why does receiving from sock1 is interrupted if a request is received from sock2, while i am receiving from sock1 (fast messages), i expect interleaved messages in the output based on the timestamps in the message.
Note that nearly all socket functions can block execution unless you've created the socket with the O_NONBLOCK option:
http://pubs.opengroup.org/onlinepubs/009695399/functions/sendto.html
And you'll also have to handle the case where recvfrom only gives you a partial read - unless you use MSG_WAITALL:
http://pubs.opengroup.org/onlinepubs/009695399/functions/recvfrom.html
Personally, I'd use a multi-threaded implementation which can have threads just sit and wait for data on each socket.
As to your final question:
why does receiving from sock1 is interrupted if a request is received from sock2, while i am receiving from sock1 (fast messages), i expect interleaved messages in the output based on the timestamps in the message.
You are slave to the network stack's implementation and there are nearly no guarantees about the sending or receiving of data on one socket relative to another. You are only guaranteed that the data within a socket is properly ordered.
I expect interleaved messages in the output based on the timestamps in the message.
Your expectation is without foundation. If there is data in either socket receive buffer, select() will fire. That's all you can rely on. You don't have any guarantee about timestamps being observed and ordered as between multiple sockets.

Mac/iOS cannot receive UDP broadcast packet, but can send UDP broadcast packet

I am working on a cross-platform game using C/C++ and socket. I am using UDP broadcast (e.g. send to xx.xx.xx.255 if on a C-type LAN address) for discovering of nearby players in LAN game playing.
The issue is the Mac/iOS version can not receive broadcast UDP packet that sent by others (neither from Mac/iOS , nor from windows), while windows version is able the receive broadcast packet from Mac/iOS. Thus Mac/iOS can not discover any nearby players while windows can discover all nearby players. On the same socket handle, sending/receiving normal UDP packet works on all platforms (sent to a specific address instead of the broadcast address).
Every testing device has only one NIC, which is connected to a single WIFI router. Addresses are dynamically allocated by DHCP on the WIFI router. The socket is bind to exact local address instead of 0.0.0.0, and I am using port 19190. [ Thanks for reading my question :) ]
Following is the code for initializing the socket on all platforms. I do set the SO_BROADCAST flag on Mac/iOS otherwise UDP broadcast packet won't be sent successfully.
BOOL Socket::__Create(const struct sockaddr &BindTo, int addr_len, int nSocketType, BOOL reuse_addr, int AF)
{
ASSERT(m_hSocket == INVALID_SOCKET);
m_hSocket = socket(AF, nSocketType, 0);
if(INVALID_SOCKET != m_hSocket)
{
int on = 1;
if(SOCK_STREAM == nSocketType)
{ linger l = {1,0};
VERIFY(0==::setsockopt(m_hSocket,SOL_SOCKET,SO_LINGER,(char*)&l,sizeof(linger)));
#if defined(PLATFORM_MAC) || defined(PLATFORM_IOS)
VERIFY(0==::setsockopt(m_hSocket,SOL_SOCKET,SO_NOSIGPIPE,(void *)&on, sizeof(on)));
#endif
}
#if defined(PLATFORM_MAC) || defined(PLATFORM_IOS)
else if(SOCK_DGRAM == nSocketType)
{
VERIFY(0==::setsockopt(m_hSocket,SOL_SOCKET,SO_BROADCAST,(void *)&on, sizeof(on)));
}
#endif
if(reuse_addr)
{ VERIFY(0==setsockopt(m_hSocket,SOL_SOCKET,SO_REUSEADDR,(char*)&on,sizeof(on)));
}
if(0==bind(m_hSocket,&BindTo,addr_len))
{
return TRUE;
}
}
_LOG_WARNING("Socket Error = "<<GetLastError());
Close();
return FALSE;
}
Here is the receiving code:
BOOL Socket::__RecvFrom(LPVOID pData, UINT len, UINT& len_out, struct sockaddr &target, int addr_len, BOOL Peek)
{
SOCKET_SIZE_T la = addr_len;
int l = (int)recvfrom(m_hSocket,(char*)pData,len,Peek?MSG_PEEK:0,&target,&la);
if(l==SOCKET_ERROR)return FALSE;
len_out = l;
return la == addr_len;
}
Did you try to bind the socket to the broadcast address (the one you are sending to) ? – ElderBug
Also, it's not working when you bind to 0.0.0.0 ? – ElderBug

Server won't connect to more than one client?

The problem is it only connects to one client instead of two. Can anyone help me figure out why?
Server:
#include <SFML/System.hpp>
#include <SFML/Network.hpp>
#include <iostream>
void sendInfo(void *UserData)
{
sf::IPAddress* ip = static_cast<sf::IPAddress*>(UserData);
// Print something...
while(true){
// Create the UDP socket
sf::SocketUDP Socket;
// Create bytes to send
char Buffer[] = "sending info.";
// Send data to "192.168.0.2" on port 4567
if (Socket.Send(Buffer, sizeof(Buffer), *ip, 4444) != sf::Socket::Done)
{
// Error...
}
}
}
void receiveInfo(void *userData)
{
// Print something...
while(true){
// Create the UDP socket
sf::SocketUDP Socket;
// Bind it (listen) to the port 4567
if (!Socket.Bind(4444))
{
// Error...
}
char Buffer[128];
std::size_t Received;
sf::IPAddress Sender;
unsigned short Port;
if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
{
// Error...
}
// Show the address / port of the sender
std::cout << Buffer << std::endl;
Socket.Close();
}
}
int main()
{
sf::IPAddress client[2];
int connected = 0;
while(connected < 2){
// Create the UDP socket
sf::SocketUDP Socket;
// Bind it (listen) to the port 4567
if (!Socket.Bind(4444))
{
// Error...
}
char Buffer[128];
std::size_t Received;
sf::IPAddress Sender;
unsigned short Port;
if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
{
// Error...
}
// Show the address / port of the sender
client[connected] = Sender;
Socket.Close();
sf::Thread* send = new sf::Thread(&sendInfo, &client[connected]);
sf::Thread* receive = new sf::Thread(&receiveInfo, &client[connected]);
// Start it !
send->Launch();
receive->Launch();
connected++;
}
while(true){
}
return EXIT_SUCCESS;
}
Client:
#include <SFML/System.hpp>
#include <SFML/Network.hpp>
#include <iostream>
void sendInfo(void *UserData)
{
// Print something...
while(true){
// Create the UDP socket
sf::SocketUDP Socket;
// Create bytes to send
char Buffer[] = "client sending info.";
// Send data to "192.168.0.2" on port 4567
if (Socket.Send(Buffer, sizeof(Buffer), "127.0.0.1", 4444) != sf::Socket::Done)
{
// Error...
}
}
}
void receiveInfo(void *userData)
{
// Print something...
while(true){
// Create the UDP socket
sf::SocketUDP Socket;
// Bind it (listen) to the port 4567
if (!Socket.Bind(4444))
{
// Error...
}
char Buffer[128];
std::size_t Received;
sf::IPAddress Sender;
unsigned short Port;
if (Socket.Receive(Buffer, sizeof(Buffer), Received, Sender, Port) != sf::Socket::Done)
{
// Error...
}
// Show the address / port of the sender
std::cout << Buffer << std::endl;
Socket.Close();
}
}
int main()
{
// Create the UDP socket
sf::SocketUDP Socket;
// Create bytes to send
char Buffer[] = "Client Joined.";
// Send data to "192.168.0.2" on port 4567
if (Socket.Send(Buffer, sizeof(Buffer), "127.0.0.1", 4444) != sf::Socket::Done)
{
// Error...
}
sf::Thread* send = new sf::Thread(&sendInfo);
sf::Thread* receive = new sf::Thread(&receiveInfo);
// Start it !
send->Launch();
receive->Launch();
while(true){
}
return EXIT_SUCCESS;
}
First things first: is this a chat server or a 'more typical' server?
If this is a chat server, then you either need to have a list of sockets that are connected to the clients (you can connect UDP sockets using the connect() call, very convenient, and it also helps reduce the chances of spoofed peers) or a list of all the client addresses that you can supply to sendto() or sendmsg().
More 'typical' servers won't try to send messages to any client except the one that most recently made a request: those servers typically don't save anything from the clients, and instead will use recvfrom() or recvmsg() to get the peer's address for use in later sendto() or sendmsg() calls.
Also, most protocols only rely on one well known port; the server uses one specific port by convention, but clients select whatever port is open and free. FTP relies heavily on well-known ports on the client-side as well, and as a result is a gigantic pain to tunnel through Network Address Translation firewalls.
It isn't just academic: both your client and your server are attempting to bind() to port 4444. This means you need at least two IP addresses on a single machine to test, or use virtualization software to run an entirely separate machine on the same hardware, or just have two machines available. It's more work than it needs to be, and there's no reason for the clients to care about their local port numbers:
Server:
// Bind it (listen) to the port 4567
if (!Socket.Bind(4444))
{
// Error...
}
Client:
// Bind it (listen) to the port 4567
if (!Socket.Bind(4444))
{
// Error...
}
Poof! These two will never run on the same host without significant tricks. I expect your "it connects to one" is probably just the server or the client connecting to itself, but without some code to fill in those // Error blocks, it'd be tough to tell for sure.
(And while we're here, I'd like to take an aside to talk about comments; comments that simply re-state what the code does aren't very useful. You'll note that most of your comments are in fact wrong, referring to wrong IPs or ports. Some just don't add any information:
// Create the UDP socket
sf::SocketUDP Socket;
I know we're taught to add comments, but sadly we're not always taught what kind of comments to add. The only comment in both programs that I'd recommend even keeping would be this one, slightly amended:
// udp doesn't require listen or accept
if (!Socket.Bind(4444))
It isn't obvious from reading the code, and won't be wrong when the port number is read out of an environment variable, command line parameter, configuration file, or registry. (It might be too redundant in a team of people familiar with the sockets API, but might be gold for a programmer not that familiar with the differences between UDP and TCP.)
Good function names, variable names, etc., will win over comments almost every time. End of aside. :)
And now, the more minor nit-picking: your thread handlers are doing some tasks like this:
while(1) {
socket s;
bind s;
r = recv s;
print r;
close s;
}
This needless creation, binding, and closing, is all wasted energy, both the computer's energy and (much more importantly) your energy. Consider the following two re-writings:
recv_thread() {
socket s;
bind s;
while (1) {
r = recv s;
print r;
}
close s;
}
or
recv_thread(s) {
while (1) {
r = recv s;
print r;
}
}
/* ... */
socket s;
bind s;
sf::Thread* rt = new sf::Thread(&recv_thread);
rt->Launch(s);
The first option is a simple refactoring of your existing code; it keeps the socket creation and destruction in the thread function, but moves the loop invariants out of the loop. The code inside the loop now does only what is necessary.
The second option is a more drastic reworking: it moves the socket creation to the main thread, where error-handling is probably much easier, and the thread function only does exactly what the remote peer needs that thread to do. (If you wanted to change from UDP to TCP, the second option would be by far the much easier easier one to change -- your threading code might not need any modifications at all.)
I hope this helps. :)