Sign in to follow this  
NukeCorr

SDL_net client-server problem

Recommended Posts

Hey.

I found a example of server and client code from the net and tried to modify and implement it to my own project but there seems to be something wrong with the sending and receiving.

server code
[code]#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>

#include "SDL.h"
#include "SDL_thread.h"
#include "SDL_net.h"
#include "chat.h"

using namespace std;

SDL_Surface *screen,*back;
bool finished = false;

SDL_Thread *net_thread=NULL;
static TCPsocket servsock = NULL;
static SDLNet_SocketSet socketset = NULL;
static struct {
int active;
TCPsocket sock;
IPaddress peer;
//Uint8 name[256+1];
} people[GAME_MAXPEOPLE];

//************IMAGE STUFF****************
SDL_Surface * ImageLoad(char *file)
{
SDL_Surface *temp1, *temp2;
temp1 = SDL_LoadBMP(file);
temp2 = SDL_DisplayFormat(temp1);
SDL_FreeSurface(temp1);
return temp2;
}
// Blit an image on the screen surface
void DrawIMG(SDL_Surface *img, int x, int y)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
SDL_BlitSurface(img, NULL, screen, &dest);
}
//******************************************

void findInactivePersonSlot(int &which){
/* Look for inactive person slot */
for ( which=0; which<GAME_MAXPEOPLE; ++which ) {
if ( people[which].sock && ! people[which].active ) {
/* Kick them out.. */
unsigned char data = GAME_BYE;
SDLNet_TCP_Send(people[which].sock, &data, 1);
SDLNet_TCP_DelSocket(socketset, people[which].sock);
SDLNet_TCP_Close(people[which].sock);
#ifdef DEBUG
fprintf(stderr, "Killed inactive socket %d\n", which);
#endif
break;
}
}
}

void roomFull(TCPsocket newsock)
{
/* No more room... */
char data = GAME_BYE;

SDLNet_TCP_Send(newsock, &data, 1);
SDLNet_TCP_Close(newsock);

#ifdef DEBUG
fprintf(stderr, "Connection refused - server is full\n");
#endif
}

void addInactiveSocket(int which, TCPsocket newsock)
{
/* Add socket as an inactive person */
people[which].sock = newsock;
people[which].peer = *SDLNet_TCP_GetPeerAddress(newsock);
SDLNet_TCP_AddSocket(socketset, people[which].sock);
#ifdef DEBUG
fprintf(stderr, "New inactive socket %d\n", which);
#endif
}

void HandleServer(void)
{
int which;
TCPsocket newsock;

newsock = SDLNet_TCP_Accept(servsock);
if ( newsock == NULL ) {
return;
}

/* Look for unconnected person slot */
for ( which=0; which<GAME_MAXPEOPLE; ++which ) {
if ( ! people[which].sock ) {
break;
}
}
if ( which == GAME_MAXPEOPLE ) {
findInactivePersonSlot(which);
}
if ( which == GAME_MAXPEOPLE ) {
roomFull(newsock);
} else {
addInactiveSocket(which, newsock);
}
}

/* Send a "new client" notification */
void SendNew(int about, int to)
{
char data[512];
//int n;

//n = strlen((char *)people[about].name)+1;
data[0] = GAME_ADD;
data[1] = about;
memcpy(&data[GAME_ADD_HOST], &people[about].peer.host, 4);
memcpy(&data[GAME_ADD_PORT], &people[about].peer.port, 2);
//data[GAME_ADD_NLEN] = 0;
//memcpy(&data[GAME_ADD_NAME], people[about].name, n); //if more info, add it here on next line ie appearance/colour
printf("SENDING DATA TO ID %d ABOUT ID %d\n",to,about);
SDLNet_TCP_Send(people[to].sock, data, 8);
}

void SendID(int which)
{
cout << "Player with ID " << which << " joined the game." << endl;
char data[512];
data[0] = GAME_ID;
data[1] = which;
printf("SENDING DATA %d TO ID %d\n",data[0],data[1]);
SDLNet_TCP_Send(people[which].sock,data,GAME_ID_LEN);
}

void notifyAllConnectionClosed(char data[], int which)
{
/* Notify all active clients */
#ifdef DEBUG
fprintf(stderr, "Closing socket %d (was%s active)\n",
which, people[which].active ? "" : " not");
#endif
if ( people[which].active ) {
people[which].active = 0;
data[0] = GAME_DEL;
data[GAME_DEL_SLOT] = which;
for (int i=0; i<GAME_MAXPEOPLE; ++i ) {
if ( people[i].active ) {
SDLNet_TCP_Send(people[i].sock,data,GAME_DEL_LEN);
}
}
}
}

void deleteConnection(int which)
{
SDLNet_TCP_DelSocket(socketset, people[which].sock);
SDLNet_TCP_Close(people[which].sock);
people[which].sock = NULL;
}

void HandleClient(int which)
{
char data[512];

/* Has the connection been closed? */
if(SDLNet_TCP_Recv(people[which].sock, data, 512) <= 0)
{
notifyAllConnectionClosed(data, which);
deleteConnection(which);
}
else
{
switch (data[0])
{
case GAME_HELLO:
{
/* Yay! An active connection */
memcpy(&people[which].peer.port,
&data[GAME_HELLO_PORT], 2);
//memcpy(people[which].name,
// &data[GAME_HELLO_NAME], 256);
//people[which].name[256] = 0;

/* Notify all active clients */
for (int i=0; i<GAME_MAXPEOPLE; ++i )
{
if ( people[i].active > 0 )
{
SendNew(which, i);
}
}

/* Notify about all active clients */
people[which].active = 1;
for (i=0; i<GAME_MAXPEOPLE; ++i )
{
if ( people[i].active > 0 )
{
SendNew(i, which);
}
}

/*Tell player which one in the slot they are */
SendID(which);
}
break;
default:
{
/* Unknown packet type?? */;
}
break;
}
}
}

void checkIfFinished(){
// Check if we have some interesting events...
SDL_Event event;
while(SDL_PollEvent(&event))
{
// Has a key been pressed down?
if(event.type == SDL_KEYDOWN)
{
// If it was escape then quit
if(event.key.keysym.sym == SDLK_ESCAPE)
{
//we are quiting the server now
finished = 1;
}
}
}
}

static void cleanup(int exitcode)
{
if ( servsock != NULL ) {
SDLNet_TCP_Close(servsock);
servsock = NULL;
}
if ( socketset != NULL ) {
SDLNet_FreeSocketSet(socketset);
socketset = NULL;
}
SDLNet_Quit();
SDL_Quit();
cout<<"Exitcode "<<exitcode<<endl;
exit(exitcode);
}

void initSDL(){
/* Initialize SDL */
if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
exit(1);
}

/* Set a 640x480 video mode*/
screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
if ( screen == NULL ) {
fprintf(stderr, "Couldn't set video mode: %s\n",SDL_GetError());
SDL_Quit();
exit(1);
}
}

void initSDLNet(){
/* Initialize the network */
if ( SDLNet_Init() < 0 ) {
fprintf(stderr, "Couldn't initialize net: %s\n",
SDLNet_GetError());
SDL_Quit();
exit(1);
}
}

void initChannels(){
/* Initialize the channels */
for (int i=0; i<GAME_MAXPEOPLE; ++i ) {
people[i].active = 0;
people[i].sock = NULL;
}
}

void allocateSockets(){
/* Allocate the socket set */
socketset = SDLNet_AllocSocketSet(GAME_MAXPEOPLE+1);
if ( socketset == NULL ) {
fprintf(stderr, "Couldn't create socket set: %s\n",
SDLNet_GetError());
cleanup(2);
}
}

void createServerSocket(){
IPaddress serverIP;

/* Create the server socket */
SDLNet_ResolveHost(&serverIP, NULL, GAME_PORT);
printf("Server IP: %x Port: %d\n", serverIP.host, serverIP.port);
servsock = SDLNet_TCP_Open(&serverIP);
if ( servsock == NULL ) {
fprintf(stderr, "Couldn't create server socket: %s\n",
SDLNet_GetError());
cleanup(2);
}
SDLNet_TCP_AddSocket(socketset, servsock);
}

void initServer(){
initSDL();
initSDLNet();
initChannels();
allocateSockets();
createServerSocket();
}

// the network input loop
int net_thread_main(void *data)
{
while(!finished){

/* Wait for events */
/*This function here is why we need a thread if we are doing keyboard
input and drawing to the screen. This function waits until something
interesting happens. So it will sit at this line until a user connects
or something */
SDLNet_CheckSockets(socketset, ~0);

/* Check for new connections */
if ( SDLNet_SocketReady(servsock) ) {
HandleServer();
}

/* Check for events on existing clients */
for (int i=0; i<GAME_MAXPEOPLE; ++i ) {
if ( SDLNet_SocketReady(people[i].sock) )
{
printf("SENDING PACKETS TO ID %d\n",i);
HandleClient(i);
}
}
}
return(0);
}

int main(int argc, char *argv[])
{
initServer();
cout<<"Server started successfully"<<endl;

//create net_thread to deal with network events
net_thread=SDL_CreateThread(net_thread_main,servsock);
if(!net_thread)
{
printf("SDL_CreateThread: %s\n",SDL_GetError());
cleanup(9);
}

// We also load in all the graphics...
back = ImageLoad("data/bg.bmp");

SDL_WM_SetCaption("Tantere 2 Server",NULL);

/* Loop, waiting for network events */
while ( !finished ) {
//for clean exiting
checkIfFinished();
if(finished){
//kill net_thread as it may be stuck waiting for events
cout <<"Attempting to kill"<<endl;
SDL_KillThread(net_thread);
}
//draw background
DrawIMG(back, 0,0);
// Filp the screen - if you dont, you wont see anything!
SDL_Flip(screen);


}
//make sure this thread is dead, before cleanup
cout<<"Waiting for thread to be dead"<<endl;
SDL_WaitThread(net_thread,NULL);
cleanup(0);

return 0;
}[/code]

client code
[code]#include "preheader.h"
#include "game.h"

void CGame::allocatePackets()
{
packets = SDLNet_AllocPacketV(4, GAME_PACKETSIZE);
}

void CGame::initClient()
{
allocatePackets();
}

void CGame::connectToHost()
{
std::cout << "Connecting to " << server << ":" << GAME_PORT << endl;
SDLNet_ResolveHost(&serverIP, server, GAME_PORT);

if(serverIP.host == INADDR_NONE)
{
std::cout << "Couldn't resolve hostname" << endl;
}
else
{
tcpsock = SDLNet_TCP_Open(&serverIP);
if(tcpsock == NULL)
{
std::cout << "Connecting to " << server << ":" << GAME_PORT << " failed." << endl;
Npc[player].Init(MAP_SIZE / 2,MAP_SIZE / 2,2);
}
else
{
std::cout << "Connected to " << server << ":" << GAME_PORT << endl;
}
}
}

void CGame::tryPorts()
{
/* Try ports in the range {GAME_PORT - GAME_PORT+10} */
for(int i=0;(udpsock == NULL) && i<10;++i)
{
udpsock = SDLNet_UDP_Open(GAME_PORT+i);
}
if(udpsock == NULL)
{
SDLNet_TCP_Close(tcpsock);
tcpsock = NULL;
std::cout << "Couldn't create UDP endpoint" << endl;
}
}

void CGame::allocateSocketSet()
{
/* Allocate the socket set for polling the network */
socketset = SDLNet_AllocSocketSet(2);

SDLNet_TCP_AddSocket(socketset, tcpsock);
SDLNet_UDP_AddSocket(socketset, udpsock);
}

bool CGame::HandleNet(void)
{
int numready, numpkts;

numready = SDLNet_CheckSockets(socketset,0);

printf("%d sockets active\n",numready);

if(SDLNet_SocketReady(tcpsock))
{
HandleServer();
}

if(SDLNet_SocketReady(udpsock))
{
HandleClient();
}
else
printf("fail\n",numready);

return true;
}

void CGame::HandleServer(void)
{
Uint8 data[512];
int pos, len;
int used;

/* Has the connection been lost with the server? */
// A non-blocking way of using this function is to check the socket with
// SDLNet_CheckSockets and SDLNet_SocketReady and call SDLNet_TCP_Recv only
// if the socket is active.
len = SDLNet_TCP_Recv(tcpsock, (char*)data, 512);

if ( len <= 0 )
{
SDLNet_TCP_DelSocket(socketset, tcpsock);
SDLNet_TCP_Close(tcpsock);
tcpsock = NULL;
std::cout<<"Connection with server lost!\n";
}
else
{
pos = 0;
while ( len > 0 )
{
used = HandleServerData(&data[pos]);

printf("LEN IS %d USED IS %d\n",len,used);
printf("CLEAN DATA RECEIVED %d %d\n",data[0],data[1]);

pos += used;
len -= used;
if ( used == 0 )
{
/* We might lose data here.. oh well,
we got a corrupt packet from server
*/
printf("CORRUPT DATA RECEIVED! %d %d %d \n",pos,len,used);
len = 0;
}
}
}
}
void CGame::HandleClient(void)
{
int n;

printf("test \n");

n = SDLNet_UDP_RecvV(udpsock, packets);
while ( n-- > 0 ) {
if ( packets[n]->channel >= 0/* && packets[n]->channel != player*/)
{
//we receive data from any messages sent out to us
string data = (char*)packets[n]->data;

std::istringstream iss(data, istringstream::in);
std::string tmp;

iss >> tmp;

//std::cout << player << " HANDLING PACKET " << tmp << endl;

//if this is a position type message
if(tmp == "pos")
{
std::string x;
iss >> x;

std::string y;
iss >> y;

std::string z;
iss >> z;

float npx, npy, npz;
npx = atof(x.c_str());
npy = atof(y.c_str());
npz = atof(z.c_str());

printf("Placing ID %d to %f %f %f \n",packets[n]->channel,npx,npy,npz);

if(player != packets[n]->channel)
Npc[packets[n]->channel].SetPos(npx,npy,npz);
}
}
}
}

int CGame::HandleServerData(Uint8 *data)
{
int used;

printf("QUERYING DATA %d\n",data[0]);

switch (data[0])
{
case GAME_ADD:
{
Uint8 which;
IPaddress newip;

/* Figure out which channel we got */
which = data[1];
printf("PLAYER ADDED TO GAME WITH ID %d\n",which);
if ((which >= GAME_MAXPEOPLE) || Npc[which].active > 0)
{
/* Invalid channel?? */
break;
}
/* Get the client IP address */
newip.host=SDLNet_Read32(&data[GAME_ADD_HOST]);
newip.port=SDLNet_Read16(&data[GAME_ADD_PORT]);

/* Copy name into channel */
//memcpy(Npc[which].name, &data[GAME_ADD_NAME], 256);
//Npc[which].name[256] = 0;

Npc[which].Init(MAP_SIZE / 2,MAP_SIZE / 2,2);

/* Let the user know what happened */
/*std::cout << " New client on ID " << which << " from "
<< ((newip.host>>24)&0xFF) <<
" " << ((newip.host>>16)&0xFF) <<" "<<
((newip.host>>8)&0xFF)<<" "<< (newip.host&0xFF)<<" "<< (newip.port)<< endl;*/
//std::cout << " New client on ID " << which <<
// " with name " << Npc[which].name << endl;

//If the player could chose an appearance, this is where you would
//load it up. But since we are not, just load the default
/*people[which].playerBase.init("data/pixman");
people[which].player.init(&people[which].playerBase,screen);
people[which].player.set(300,220);
people[which].player.setSpeed(1);*/

/* Put the address back in network form */
newip.host = SDL_SwapBE32(newip.host);
newip.port = SDL_SwapBE16(newip.port);

/* Bind the address to the UDP socket */
SDLNet_UDP_Bind(udpsock, which, &newip);
}
used = 8;
break;
case GAME_ID:
{
Uint8 which;
/* Figure out which channel we got */
std::cout << "Your ID on the server is " << data[GAME_ID_SLOT] << endl;
which = data[GAME_ID_SLOT];
if ((which >= GAME_MAXPEOPLE)/* || Npc[which].active > 0*/)
{
printf("GAME_ID FAILED, NPC %d ACTIVE \n",which);
break;
}

player = which;
Npc[player].Init(MAP_SIZE / 2,MAP_SIZE / 2,2);
//cout<< "You are player "<<yourPlayer<<" "<<people[yourPlayer].name<<endl;
}
used = GAME_ID_LEN;
break;
case GAME_DEL:
{
Uint8 which;

/* Figure out which channel we lost */
which = data[GAME_DEL_SLOT];
if ( (which >= GAME_MAXPEOPLE) || Npc[which].active < 1 )
{
/* Invalid channel?? */
break;
}
Npc[which].Delete();

/* Let the user know what happened */
std::cout << "ID " << which << " left the game" << endl;

/* Unbind the address on the UDP socket */
SDLNet_UDP_Unbind(udpsock, which);
}
used = GAME_DEL_LEN;
break;
case GAME_BYE: {
std::cout<<"Server is full\n";
}
used = GAME_BYE_LEN;
break;
default:
{
/* Unknown packet type?? */;
printf("UNKNOWN PACKET TYPE! CORRUPTED? %d %d\n",data[0],data[1]);
}
used = 0;
break;
}
return(used);
}

void CGame::SendHello(char *name)
{
IPaddress *myip;
char hello[1+1+256];
int i, n;

if ( tcpsock != NULL )
{
if ( (name == NULL) &&
((name=getenv("GAME_USER")) == NULL) &&
((name=getenv("USER")) == NULL ) )
{
name="Unknown";
}
std::cout<<"Using name "<< name <<endl;

hello[0] = GAME_HELLO;
myip = SDLNet_UDP_GetPeerAddress(udpsock, -1);
memcpy(&hello[GAME_HELLO_PORT], &myip->port, 2);
if ( strlen(name) > 255 )
{
n = 255;
}
else
{
n = strlen(name);
}
hello[GAME_HELLO_NLEN] = n;
strncpy(&hello[GAME_HELLO_NAME], name, n);
hello[GAME_HELLO_NAME+n++] = 0;

SDLNet_TCP_Send(tcpsock, hello, GAME_HELLO_NAME+n);
}
}

void CGame::sendPos()
{
if(Npc[player].active)
{
float x = Npc[player].x;
float y = Npc[player].y;
float z = Npc[player].z;
//std::cout << packets[0]->maxlen << endl;
char *msg = new char[packets[0]->maxlen];
//char msg[256];
sprintf(msg, "pos %f %f %f", x, y, z);
std::cout << "Sending packet " << msg << endl;

for (int i=0; i < GAME_MAXPEOPLE; i++ )
{
if(Npc[i].active)
{
memcpy(packets[0]->data, msg, packets[0]->maxlen);
packets[0]->len = packets[0]->maxlen;

int numsent = SDLNet_UDP_Send(udpsock, i, packets[0]);
if(!numsent)
printf("SDLNet_UDP_Send: %s\n",SDLNet_GetError());

//printf("%s %d \n",packets[0]->data,packets[0]->len);
}
}
}
}[/code]

[code]/* Convert four letters into a number */
#define MAKE_NUM(A, B, C, D) (((A+B)<<8)|(C+D))

/* Defines for the chat client */
#define GAME_SCROLLBACK 512 /* Save 512 lines in scrollback */
#define GAME_PROMPT "> "
#define GAME_PACKETSIZE 256 /* Maximum length of a message */

/* Defines shared between the server and client */
#define GAME_PORT MAKE_NUM('C','H','A','T')

/* The protocol between the chat client and server */
#define GAME_HELLO 0 /* 0+Port+len+name */
#define GAME_HELLO_PORT 1
#define GAME_HELLO_NLEN GAME_HELLO_PORT+2
#define GAME_HELLO_NAME GAME_HELLO_NLEN+1
#define GAME_ADD 1 /* 1+N+IP+Port+len+name */
#define GAME_ADD_SLOT 1
#define GAME_ADD_HOST GAME_ADD_SLOT+1
#define GAME_ADD_PORT GAME_ADD_HOST+4
#define GAME_DEL 2 /* 2+N */
#define GAME_DEL_SLOT 1
#define GAME_DEL_LEN GAME_DEL_SLOT+1
#define GAME_BYE 255 /* 255 */
#define GAME_BYE_LEN 1
#define GAME_ID 5
#define GAME_ID_SLOT 1
#define GAME_ID_LEN 2

/* The maximum number of people who can talk at once */
#define GAME_MAXPEOPLE 10[/code]

in the main gameloop I call
[code]HandleNet();
sendPos();[/code]

It works fine when server and two clients are running on same computer. Over the internet clients can connect to server but do not receive other players' positions (Npcs)

I think there's problem with my packet lengths, could anyone tell where the problem is?

Cheers.

Share this post


Link to post
Share on other sites
It looks like the SDL code is overly complicated. I suggest trying a simple library like Enet http://enet.bespin.org/index.html, or http://nolimitsdesigns.com/UDP_Engine/html/index.html. You might have better luck there.

Share this post


Link to post
Share on other sites
[quote name='NukeCorr' timestamp='1305994812' post='4813880']
I found a example of server and client code from the net and tried to modify and implement it to my own project but there seems to be something wrong with the sending and receiving.

[/quote]

Your code looks way too complicated for what it needs to do. For example, why are you opening both a TCP and a UDP socket?

If you capture your network data with Wireshark, what do you see on the wire? Do you see both the send and return data you expect? Have you set up port forwarding on both ends, and made sure there is no firewall in the way?
Also, it doesn't look like your TCP receive loop buffers half-received packets for the next turn. You want to fix that for any operation, although this issue is very likely to hit you over the internet, and less likely to hit you over a local connection.

Also, it doesn't look as if SDL_Net gives you anything better than just plain sockets/WinSock here -- why not use that? Or, if you want C++, why note use boost::asio?
Or, why not use a higher-layer packet library, like RakNet, Enet, etc?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this