Developer's Guide to Multiplayer Games
I am just wondering if anybody has read Andrew Mulholland''s
book, "Developer''s Guide to Multiplayer Games". I tried the
code on the CD and even implemented a network program using
the network library provided. The problem is that it works
perfectly on the local network but doesn''t work very well
on the Internet with a "game server connection error" most
of the time. I have a feeling that the network library (both
TCP and UPD using Winsock) is at fault because I have tried
hosting the server on different computers (even on friends''
computers in different countries) but the result is always
the same - works fine in the local area network but unreliable
over the Internet (possibly works for people from countries
that are nearer). A network latency problem perhaps? I am
still unsure as the authors should have figured it out
while working on the network library. Any idea what is wrong
or how I can contact the authors of the book?
Thanks ^_^.
_________________
Best regards,
Sherman Chin
www.Sherman3D.com
_________________ Best regards, Sherman Chin Director Sherman3D (Malaysia) Sdn Bhd www.Sherman3D.com www.AlphaKimori.com
The problem is inherent with the connections you are implementing. TCP/IP is a guaranteed connection and delivery. You usually need to check the length of the packet that you sent to make sure that all of it arrived. Sometimes it doesn''t. It''s guaranteed to get there, but I don''t think it''s guaranteed to get there all in one piece. The other part that''s guaranteed is the ordering. Send packet1 then packet2. Packet1 is guaranteed to arrive before packet2.
UDP is a connectionless packet. You''re not guaranteed that the packet will get there in one piece, in the right order, or even that it will make it. If you send packet1 then packet2, they might arrive at the other end in that order, they might not. Both might get through, one might get through, or neither might get through. UDP is faster in terms of network overhead on the machine and better suited for quick, throwaway messages, but you should never rely on it for guaranteed messaging.
I''m sure that some of my explanation is a bit off, and someone else who has had more experience in this area would have a better explanation, but that should at least point you in the right direction.
Looking for an honest video game publisher? Visit www.gamethoughts.com
UDP is a connectionless packet. You''re not guaranteed that the packet will get there in one piece, in the right order, or even that it will make it. If you send packet1 then packet2, they might arrive at the other end in that order, they might not. Both might get through, one might get through, or neither might get through. UDP is faster in terms of network overhead on the machine and better suited for quick, throwaway messages, but you should never rely on it for guaranteed messaging.
I''m sure that some of my explanation is a bit off, and someone else who has had more experience in this area would have a better explanation, but that should at least point you in the right direction.
Looking for an honest video game publisher? Visit www.gamethoughts.com
Shameless plug: Game Thoughts
July 15, 2002 05:51 PM
Hmmm, i don''t think thats quite what the problem is, or indeed much related to the problem. "game server connection error" seems to indicate that the network application is having issues with contacting the server (ie. connect()). Are you behind a firewall? Ummm, it''s very hard to diagnose these problems when you know nothing of the underlying code, or why/when an error message occurs
Perhaps if you could post some of the code from the network library that you''re using??
![](tongue.gif)
Server-side code extract:
// Create a network socket object from our NET library.
NET_Socket MOGSocket;
// Create our server with given port number and protocol type.
if(MOGSocket.CreateServer(serverPort, NET_TCP) != 0)
{
LogString("Failed to Create the Server");
return 1;
}
// Timed thread message from our network library to
// accept network messages.
case USMSG_ACCEPT:
// Use our network library to process network messages.
MOGSocket.AcceptConnection();
// Return success.
return 0;
Client-side code extract:
// Create a network socket object from our NET library.
NET_Socket serverSocket;
// Connect to the server
if (
serverSocket.ConnectToServer(serverIP, serverPort, NET_TCP)
== NET_INVALID_SOCKET
)
{
MessageBox(NULL, "Game Server Connection Error", "Fatal Error", MB_OK);
ShowWindow(hWnd_LoginDialog,SW_SHOW);
return 1;
}
As you can see, the problem lies with ConnectToServer() which is
a function in the network library created by Andrew Muholland and
Hakala in their book "Developer''s Guide To Multiplayer Games". Here
is the extract from their network library:
int NET_Socket::ConnectToServer(char *addressText, int port, int Protocol)
{
SOCKET sock;
struct sockaddr_in inetServAddr;
NETMSGDATA_DATASIZE DataMsg;
memset(&DataMsg, 0, sizeof(NETMSGDATA_DATASIZE));
// Check which protocol to use
switch(Protocol)
{
// TCP protocol
case NET_TCP:
sock = socket(AF_INET, SOCK_STREAM, 0);
break;
// UDP protocol
case NET_UDP:
sock = socket(AF_INET, SOCK_DGRAM, 0);
break;
default:
return NET_INVALID_PROTOCOL;
}
if(sock == NET_INVALID_SOCKET)
{
return -1;
}
// Create inet_addr from the IP number
u_long inetAddr = inet_addr(addressText);
// Fill the address information
memset((char *) &inetServAddr, 0, sizeof(inetServAddr));
inetServAddr.sin_family = AF_INET;
inetServAddr.sin_port = htons((u_short) port);
inetServAddr.sin_addr.s_addr = inetAddr;
// Connect using TCP
if(Protocol == NET_TCP)
{
int error = connect(sock, (struct sockaddr *) &inetServAddr,
sizeof(inetServAddr));
// This works only when using TCP
if(error != 0)
{
LogString("ERROR: connect() error (error = %d)", error);
return NET_INVALID_SOCKET;
}
}
#ifdef WIN32
// Create Win32 socket input event handle
RemoteHost[NETID_SERVER].SocketInputEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
// Fill remote host (server) info
RemoteHost[NETID_SERVER].socket = sock;
RemoteHost[NETID_SERVER].inetSockAddr = inetServAddr;
RemoteHost[NETID_SERVER].addrLen = (socklen_t) sizeof(inetServAddr);
RemoteHost[NETID_SERVER].online = 1;
RemoteHosts++;
char *string;
string = inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);
LogString("Sending knock to socket %d, ip: %s, port %d",
RemoteHost[NETID_SERVER].socket, string,
ntohs(RemoteHost[NETID_SERVER].inetSockAddr.sin_port));
// Knock if using UDP
if(Protocol == NET_UDP)
{
NETMSG_GENERIC KnockMsg;
// Fill the message data
KnockMsg.type = NETMSG_KNOCK;
KnockMsg.toId = NETID_SERVER;
KnockMsg.fromId = NETID_UNKNOWN;
DataMsg.type = NETMSG_DATASIZE;
DataMsg.size = sizeof(NETMSG_GENERIC);
char *string;
string = inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);
LogString("Writing knock to socket %d, addrLen %d ip %s",
RemoteHost[NETID_SERVER].socket, RemoteHost[NETID_SERVER].addrLen,
string);
// Write the knock message
int bytes = WriteTo(RemoteHost[NETID_SERVER].socket,
(char *) &KnockMsg, sizeof(NETMSG_GENERIC),
(struct sockaddr *) &RemoteHost[NETID_SERVER].inetSockAddr,
sizeof(RemoteHost[NETID_SERVER].inetSockAddr));
if(!bytes) return NET_INVALID_SOCKET;
}
NETMSG_GENERIC *Msg;
char *recvbuf;
recvbuf = (char *) malloc(NET_BUFFSIZE);
memset((char *) recvbuf, 0, sizeof(char));
LogString("Waiting for localId");
// Set the remote host (server) non-blocking
SetNonBlocking(NETID_SERVER, 1);
int counter = 0;
// Loop while we do not have a local ID number
while(localId < 1)
{
counter++;
// Check if we are here too long, we probably cannot connect to anywhere.
if(counter == 100000)
{
LogString("ERROR: We were waiting for localId for too long");
return NET_INVALID_SOCKET;
}
struct sockaddr_in newAddr;
socklen_t newLen = sizeof(newAddr);
memset((char *) recvbuf, 0, sizeof(char));
// Read data from the server
if(ReadFrom(RemoteHost[NETID_SERVER].socket, recvbuf,
sizeof(NETMSGDATA_DATASIZE),
(struct sockaddr *) &newAddr,
&newLen, 1))
{
Msg = (NETMSG_GENERIC *) recvbuf;
// Update server''s address
if(Protocol == NET_UDP)
{
RemoteHost[NETID_SERVER].inetSockAddr = newAddr;
RemoteHost[NETID_SERVER].addrLen = newLen;
}
switch(Msg->type)
{
// Got local ID number
case NETMSG_GIVEID:
NETMSGDATA_DATASIZE *idMsg;
idMsg = (NETMSGDATA_DATASIZE *) Msg;
// Set the local ID number
localId = idMsg->toId;
string =
inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);
LogString("The server is now on socket %d, ip %s, port %d",
RemoteHost[NETID_SERVER].socket,
string,
ntohs(RemoteHost[NETID_SERVER].inetSockAddr.sin_port));
NETMSG_CONF ConfMsg;
ConfMsg.conf = localId;
// If using UDP, send confirmation of local ID number
if(Protocol == NET_UDP)
{
LogString("Writing confirmation of localId (localId:
%d)", localId);
WriteTo(RemoteHost[NETID_SERVER].socket, (char *)
&ConfMsg,
sizeof(NETMSG_CONF),
(struct sockaddr *)
&RemoteHost[NETID_SERVER].inetSockAddr,
sizeof(RemoteHost[NETID_SERVER].inetSockAddr));
}
break;
default:
continue;
}
}
}
free(recvbuf);
recvbuf = NULL;
LogString("Got localId %d", localId);
// Set the local host online
Online = 1;
FindNextFreeHostIndex();
param.id = NETID_SERVER;
param.netParamSocket = this;
// Start the IO thread
#ifdef WIN32
DWORD threadId;
WSAAsyncSelect(sock, hWnd_netLib, 0, 0);
// Monitor incoming network events
WSAEventSelect(sock, RemoteHost[NETID_SERVER].SocketInputEvent,
FD_READ);
HANDLE threadHandle = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) NET_WinIOThreadFunc,
(void *) ¶m, 0, &threadId);
#else
pthread_t threadId;
pthread_create(&threadId, NULL, NET_UnixIOThreadFunc, (void *) ¶m);
#endif
// Wait for the thread to start
while(!RemoteHost[NETID_SERVER].thread);
return 0;
}
void NET_Socket::AcceptConnection(void)
{
SOCKET connectSock = NET_INVALID_SOCKET;
struct sockaddr_in clientAddr;
socklen_t clientLen;
// Loop as long as the connection has not been accepted
while(connectSock == NET_INVALID_SOCKET)
{
clientLen = sizeof(clientAddr);
connectSock = accept(listenSocket, (struct sockaddr *) &clientAddr, &clientLen);
}
LogString("Accepted a connection");
LogString("Connection on socket %d", connectSock);
// Update the next free host index number
FindNextFreeHostIndex();
// Set the socket non-blocking
SetNonBlocking(NextFreeHostIndex, 1);
#ifdef WIN32
// Create input event handle
RemoteHost[NextFreeHostIndex].SocketInputEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
// Fill remote host information
RemoteHost[NextFreeHostIndex].socket = connectSock;
RemoteHost[NextFreeHostIndex].inetSockAddr = clientAddr;
RemoteHost[NextFreeHostIndex].addrLen = clientLen;
RemoteHost[NextFreeHostIndex].online = 1;
RemoteHosts++;
// Give local id number to the client
GiveLocalIdTCP();
// Fill parameter information for the thread
param.id = NextFreeHostIndex;
param.netParamSocket = this;
// Start the IO thread
#ifdef WIN32
// Stop notifying for incoming connections on this socket
WSAAsyncSelect(connectSock, hWnd_netLib, 0, 0);
// Monitor incoming network events
WSAEventSelect(connectSock, RemoteHost[NextFreeHostIndex].SocketInputEvent,
FD_READ);
// Create the thread
HANDLE threadHandle = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) NET_WinIOThreadFunc,
(void *) ¶m, 0, NULL);
#else
// Create the thread
pthread_create(NULL, NULL, NET_UnixIOThreadFunc, (void *) ¶m);
#endif
}
// Create a network socket object from our NET library.
NET_Socket MOGSocket;
// Create our server with given port number and protocol type.
if(MOGSocket.CreateServer(serverPort, NET_TCP) != 0)
{
LogString("Failed to Create the Server");
return 1;
}
// Timed thread message from our network library to
// accept network messages.
case USMSG_ACCEPT:
// Use our network library to process network messages.
MOGSocket.AcceptConnection();
// Return success.
return 0;
Client-side code extract:
// Create a network socket object from our NET library.
NET_Socket serverSocket;
// Connect to the server
if (
serverSocket.ConnectToServer(serverIP, serverPort, NET_TCP)
== NET_INVALID_SOCKET
)
{
MessageBox(NULL, "Game Server Connection Error", "Fatal Error", MB_OK);
ShowWindow(hWnd_LoginDialog,SW_SHOW);
return 1;
}
As you can see, the problem lies with ConnectToServer() which is
a function in the network library created by Andrew Muholland and
Hakala in their book "Developer''s Guide To Multiplayer Games". Here
is the extract from their network library:
int NET_Socket::ConnectToServer(char *addressText, int port, int Protocol)
{
SOCKET sock;
struct sockaddr_in inetServAddr;
NETMSGDATA_DATASIZE DataMsg;
memset(&DataMsg, 0, sizeof(NETMSGDATA_DATASIZE));
// Check which protocol to use
switch(Protocol)
{
// TCP protocol
case NET_TCP:
sock = socket(AF_INET, SOCK_STREAM, 0);
break;
// UDP protocol
case NET_UDP:
sock = socket(AF_INET, SOCK_DGRAM, 0);
break;
default:
return NET_INVALID_PROTOCOL;
}
if(sock == NET_INVALID_SOCKET)
{
return -1;
}
// Create inet_addr from the IP number
u_long inetAddr = inet_addr(addressText);
// Fill the address information
memset((char *) &inetServAddr, 0, sizeof(inetServAddr));
inetServAddr.sin_family = AF_INET;
inetServAddr.sin_port = htons((u_short) port);
inetServAddr.sin_addr.s_addr = inetAddr;
// Connect using TCP
if(Protocol == NET_TCP)
{
int error = connect(sock, (struct sockaddr *) &inetServAddr,
sizeof(inetServAddr));
// This works only when using TCP
if(error != 0)
{
LogString("ERROR: connect() error (error = %d)", error);
return NET_INVALID_SOCKET;
}
}
#ifdef WIN32
// Create Win32 socket input event handle
RemoteHost[NETID_SERVER].SocketInputEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
// Fill remote host (server) info
RemoteHost[NETID_SERVER].socket = sock;
RemoteHost[NETID_SERVER].inetSockAddr = inetServAddr;
RemoteHost[NETID_SERVER].addrLen = (socklen_t) sizeof(inetServAddr);
RemoteHost[NETID_SERVER].online = 1;
RemoteHosts++;
char *string;
string = inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);
LogString("Sending knock to socket %d, ip: %s, port %d",
RemoteHost[NETID_SERVER].socket, string,
ntohs(RemoteHost[NETID_SERVER].inetSockAddr.sin_port));
// Knock if using UDP
if(Protocol == NET_UDP)
{
NETMSG_GENERIC KnockMsg;
// Fill the message data
KnockMsg.type = NETMSG_KNOCK;
KnockMsg.toId = NETID_SERVER;
KnockMsg.fromId = NETID_UNKNOWN;
DataMsg.type = NETMSG_DATASIZE;
DataMsg.size = sizeof(NETMSG_GENERIC);
char *string;
string = inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);
LogString("Writing knock to socket %d, addrLen %d ip %s",
RemoteHost[NETID_SERVER].socket, RemoteHost[NETID_SERVER].addrLen,
string);
// Write the knock message
int bytes = WriteTo(RemoteHost[NETID_SERVER].socket,
(char *) &KnockMsg, sizeof(NETMSG_GENERIC),
(struct sockaddr *) &RemoteHost[NETID_SERVER].inetSockAddr,
sizeof(RemoteHost[NETID_SERVER].inetSockAddr));
if(!bytes) return NET_INVALID_SOCKET;
}
NETMSG_GENERIC *Msg;
char *recvbuf;
recvbuf = (char *) malloc(NET_BUFFSIZE);
memset((char *) recvbuf, 0, sizeof(char));
LogString("Waiting for localId");
// Set the remote host (server) non-blocking
SetNonBlocking(NETID_SERVER, 1);
int counter = 0;
// Loop while we do not have a local ID number
while(localId < 1)
{
counter++;
// Check if we are here too long, we probably cannot connect to anywhere.
if(counter == 100000)
{
LogString("ERROR: We were waiting for localId for too long");
return NET_INVALID_SOCKET;
}
struct sockaddr_in newAddr;
socklen_t newLen = sizeof(newAddr);
memset((char *) recvbuf, 0, sizeof(char));
// Read data from the server
if(ReadFrom(RemoteHost[NETID_SERVER].socket, recvbuf,
sizeof(NETMSGDATA_DATASIZE),
(struct sockaddr *) &newAddr,
&newLen, 1))
{
Msg = (NETMSG_GENERIC *) recvbuf;
// Update server''s address
if(Protocol == NET_UDP)
{
RemoteHost[NETID_SERVER].inetSockAddr = newAddr;
RemoteHost[NETID_SERVER].addrLen = newLen;
}
switch(Msg->type)
{
// Got local ID number
case NETMSG_GIVEID:
NETMSGDATA_DATASIZE *idMsg;
idMsg = (NETMSGDATA_DATASIZE *) Msg;
// Set the local ID number
localId = idMsg->toId;
string =
inet_ntoa(RemoteHost[NETID_SERVER].inetSockAddr.sin_addr);
LogString("The server is now on socket %d, ip %s, port %d",
RemoteHost[NETID_SERVER].socket,
string,
ntohs(RemoteHost[NETID_SERVER].inetSockAddr.sin_port));
NETMSG_CONF ConfMsg;
ConfMsg.conf = localId;
// If using UDP, send confirmation of local ID number
if(Protocol == NET_UDP)
{
LogString("Writing confirmation of localId (localId:
%d)", localId);
WriteTo(RemoteHost[NETID_SERVER].socket, (char *)
&ConfMsg,
sizeof(NETMSG_CONF),
(struct sockaddr *)
&RemoteHost[NETID_SERVER].inetSockAddr,
sizeof(RemoteHost[NETID_SERVER].inetSockAddr));
}
break;
default:
continue;
}
}
}
free(recvbuf);
recvbuf = NULL;
LogString("Got localId %d", localId);
// Set the local host online
Online = 1;
FindNextFreeHostIndex();
param.id = NETID_SERVER;
param.netParamSocket = this;
// Start the IO thread
#ifdef WIN32
DWORD threadId;
WSAAsyncSelect(sock, hWnd_netLib, 0, 0);
// Monitor incoming network events
WSAEventSelect(sock, RemoteHost[NETID_SERVER].SocketInputEvent,
FD_READ);
HANDLE threadHandle = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) NET_WinIOThreadFunc,
(void *) ¶m, 0, &threadId);
#else
pthread_t threadId;
pthread_create(&threadId, NULL, NET_UnixIOThreadFunc, (void *) ¶m);
#endif
// Wait for the thread to start
while(!RemoteHost[NETID_SERVER].thread);
return 0;
}
void NET_Socket::AcceptConnection(void)
{
SOCKET connectSock = NET_INVALID_SOCKET;
struct sockaddr_in clientAddr;
socklen_t clientLen;
// Loop as long as the connection has not been accepted
while(connectSock == NET_INVALID_SOCKET)
{
clientLen = sizeof(clientAddr);
connectSock = accept(listenSocket, (struct sockaddr *) &clientAddr, &clientLen);
}
LogString("Accepted a connection");
LogString("Connection on socket %d", connectSock);
// Update the next free host index number
FindNextFreeHostIndex();
// Set the socket non-blocking
SetNonBlocking(NextFreeHostIndex, 1);
#ifdef WIN32
// Create input event handle
RemoteHost[NextFreeHostIndex].SocketInputEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
// Fill remote host information
RemoteHost[NextFreeHostIndex].socket = connectSock;
RemoteHost[NextFreeHostIndex].inetSockAddr = clientAddr;
RemoteHost[NextFreeHostIndex].addrLen = clientLen;
RemoteHost[NextFreeHostIndex].online = 1;
RemoteHosts++;
// Give local id number to the client
GiveLocalIdTCP();
// Fill parameter information for the thread
param.id = NextFreeHostIndex;
param.netParamSocket = this;
// Start the IO thread
#ifdef WIN32
// Stop notifying for incoming connections on this socket
WSAAsyncSelect(connectSock, hWnd_netLib, 0, 0);
// Monitor incoming network events
WSAEventSelect(connectSock, RemoteHost[NextFreeHostIndex].SocketInputEvent,
FD_READ);
// Create the thread
HANDLE threadHandle = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) NET_WinIOThreadFunc,
(void *) ¶m, 0, NULL);
#else
// Create the thread
pthread_create(NULL, NULL, NET_UnixIOThreadFunc, (void *) ¶m);
#endif
}
_________________ Best regards, Sherman Chin Director Sherman3D (Malaysia) Sdn Bhd www.Sherman3D.com www.AlphaKimori.com
Thanks for your help guys. BTW, those are only code extracts
and the book by Mulholland and Hakala is real good if anybody
is interested. ^_^
_________________
Best regards,
Sherman Chin
www.Sherman3D.com
and the book by Mulholland and Hakala is real good if anybody
is interested. ^_^
_________________
Best regards,
Sherman Chin
www.Sherman3D.com
_________________ Best regards, Sherman Chin Director Sherman3D (Malaysia) Sdn Bhd www.Sherman3D.com www.AlphaKimori.com
July 16, 2002 10:44 AM
Only things i can think of after quickly looking over that...
I couldn''t see any dns''ing functions in there...?? It probably won''t run if you supply a hostname (google.com, localhost, etc.) instead of a dots and numbers ip address (209.123.231.1). The only other thing i can really suggest is debug their code to find out exactly where it''s failing and/or email them about the problem...
I couldn''t see any dns''ing functions in there...?? It probably won''t run if you supply a hostname (google.com, localhost, etc.) instead of a dots and numbers ip address (209.123.231.1). The only other thing i can really suggest is debug their code to find out exactly where it''s failing and/or email them about the problem...
Yup, no DNS resolution unfortunately ;_; - have to use IP
addresses directly. Do you have any idea what the e-mail
addresses of the authors are or do they even have websites?
I have done a search in google to no avail and their book
doesn''t seem to have any contact info. Thanks ^_^
_________________
Best regards,
Sherman Chin
www.Sherman3D.com
addresses directly. Do you have any idea what the e-mail
addresses of the authors are or do they even have websites?
I have done a search in google to no avail and their book
doesn''t seem to have any contact info. Thanks ^_^
_________________
Best regards,
Sherman Chin
www.Sherman3D.com
_________________ Best regards, Sherman Chin Director Sherman3D (Malaysia) Sdn Bhd www.Sherman3D.com www.AlphaKimori.com
July 16, 2002 09:23 PM
No idea, i''m surprised they don''t have an email address in the book...very strange.
I suppose the best way to find contact info might be to conatact the publisher..
I suppose the best way to find contact info might be to conatact the publisher..
Thanks again ^_^. I will do just that - wish there were an
easier solution though *sigh*.
_________________
Best regards,
Sherman Chin
www.Sherman3D.com
easier solution though *sigh*.
_________________
Best regards,
Sherman Chin
www.Sherman3D.com
_________________ Best regards, Sherman Chin Director Sherman3D (Malaysia) Sdn Bhd www.Sherman3D.com www.AlphaKimori.com
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement