Jump to content
  • Advertisement
Sign in to follow this  
ARC inc

Little Help

This topic is 3328 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ok, so I've got a packet design down an like how I have it layed out. but now I need some help with sending the packets properly in TCP.
typedef enum PacketType
{

CreateAccount   = 0x001,
CreateCharacter = 0x002,
ChangePassword  = 0x003,
MovePlayerX     = 0x004,
MovePlayerY     = 0x005,
MovePlayerJump  = 0x006,
MoveMap         = 0x007
};

typedef unsigned short word;
typedef unsigned int dword;
typedef unsigned short ushort;
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;

class PACKETS_API API_PACKETS
{
public:
API_PACKETS(void);
~API_PACKETS();
PacketType ParsePacket(char * packet);
char * BuildPacket(int type);
char * BuildPacket();
void ClearPacket();
WORD packetid;
short size;
char * buffer;

struct packet0x001{
ushort size;
ushort type;
char accountname[16];
char passwordhash[16];
}CreateAccountPacket;
 //etc etc

Basicly I am lost on how to send these packets. I want to have like lets say a send buffer of like 35kb before sending any data. Any help?

Share this post


Link to post
Share on other sites
Advertisement
I'm not sure what your actual problem is? How to implement a send buffer? Or how to actually send packets?

To implement a send buffer, allocate a per-client buffer of 35kB (which is an awful lot, by the way!). Also maintain an internal size counter which tracks how much space in the buffer you've used. For example:


#include <cstring>
#define BUFFER_SIZE 35000

class MessagetBuffer
{
public:
// Note: ctor throws std::bad_alloc!
MessageBuffer() : m_spaceUsed(0)
{ m_data = new unsigned char[BUFFER_SIZE]; }

~MessageBuffer()
{ delete[] m_data; }

void addData(const unsigned char* data, size_t size)
{
// If the add would exceed buffer size, flush first.
if(m_spaceUsed + size > BUFFER_SIZE)
flush();

memcpy(m_data + m_spaceUsed, data, size);
m_spaceUsed += size;
}

void flush()
{
// Won't send an empty buffer which would be the case
// if addData() were to be invoked for the first time
// with a size of >BUFFER_SIZE bytes.
if(m_spaceUsed == 0)
return;

// ... send logic here ...
// send bytes starting at m_data[0] to m_data[spaceUsed-1] inclusive.
// ...

m_spaceUsed = 0;
}

private:
unsigned char* m_data;
unsigned int m_spaceUsed;
};



I haven't actually tested it, but I hope you get the idea. When you create an instance of the MessageBuffer it allocates BUFFER_SIZE bytes. When you want to send a message, write it to the buffer using addData(). Make sure that each packet contains both a) its own length and b) its type, so that packets can be separated on the receiving end. If an addData() call would exceed the buffer size, the buffer is flushed. The flush operation can also be performed through the public flush() function. You will probably want to pass a socket to the constructor of the MessageBuffer so that it can be associated with the appropriate client and send to it.

Share this post


Link to post
Share on other sites
What do you already know in the area of network programming? Basic Berkeley sockets / Winsock API?

Share this post


Link to post
Share on other sites
Assuming plain Winsock:
send(sock, &packet, packet.size, 0);

Since it's TCP/IP, the protocol will take care of breaking it into smaller chunks. When you recv() the packet, you need to make sure you read the whole packet before returning it however.

Share this post


Link to post
Share on other sites
Even though it's TCP, send() may decide to send less than what you wanted, and returns the actual number of bytes sent.

In general, you want outgoing data to be kept in a cyclic buffer, where you have a "head" and a "tail." Whenever select() shows that the socket is writable, you attempt to send() as much as you can of the buffer, and advance the "tail" by however much send() actually returned.

And, because you buffer, this means that there will be a point where you want more data buffered than there is space for. You'll have to decide what to do about that, because you don't want to stall the server. Generally, I decide that a client that can't keep up has to be kicked/disconnected. I think that's a reasonable solution, as it also prevents a DOS when someone connects and then doesn't read much, leading to ever-growing buffers on the server, and eventual memory exhaustion.

Now, if you're one of the cool kids, you map your buffer into your address space twice. Say that address 0x43128000 and 43130000 are both mmapped to the same 32k-range of the same file (or anonymous block of physical memory, or whatnot), then you can read past the "break" in the cyclic buffer, without having to split the read in two. This makes cyclic buffers pretty much as convenient as a regular linear memory block, but lets you avoid any extraneous memcpy() to keep things contiguous. MMU FTW!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!