How to send non-'char*' information with winsock

Pages: 12
closed account (2NywAqkS)
How would you send and receive 'double's or 'int's across a socket because the functions send and recv only accept 'char *' buffers?
EDIT: A better way: Check my second post.
Last edited on
How do you send a struct?
To send a struct:
EDIT: Shortened Code.
You can simply do this D:

1
2
3
YourStruct a;
...
Send(SOCKET,&a,sizeof(a),...);

and receive it like this:
1
2
YourStruct a;
Recv(SOCKET,&a,sizeof(a),...);

Sorry for wasting your time, LOL.
EDIT: Also works for ints and doubles. You will just get endianness problems.
Last edited on
i[Size]=0;


I think there might be an mistake...i isn't an array o.O

And by aligned you mean what?
Last edited on
1. In fact i mistook, it wasn't i[Size] but Dest[Size].
2. You just need the last two codes, the Send(SOCKET,&a,sizeof(a)...
3. Think of a structure in a binary way like this:
1
2
3
4
0 // 1 Bit
00 // 2 Bit
0000 // 4 Bit
00000000 // 8 Bit 

From what i know, a bool uses 1 bit of size. You need a 4-byte alignment.
So if you want to send a bool, you cannot send it like any other variable:
You must align it to the first 4 bits: like this:
1
2
3
4
5
6
struct SendBool {
  bool Value; // Invalid
};
struct SendBoolOk {
  bool Value[4]; // Valid, or just use a char
};

This is what comes from my experience. I may be wrong. If i'm wrong, please could someone else explain it to me too? Lol.
lets say i'd need to send a string or at least a char*, those are terminated by \0, how could you send that?
in case of char *: Send( SOCKET, (Identifier), strlen(Identifier), ...)
(Use a 512-char limit, so you know your maximum message length)
in case of std::string: char * Pointer = Identifier.c_str(); Send(SOCKET, Pointer, strlen(Pointer), ...)
Again apply a limit to strlen(Pointer) to 512 or a similar constant value so when you receive it, you know what's the maximum size of the packet.
EDIT: Here's a little clarification:
1
2
3
4
5
6
7
8
9
10
11
12
13
void SendString(std::string String, ... /* All other data that you need to use Send */)
{
	unsigned int Length = strlen(String.c_str());
	if(Length > 512)
		Length = 512;
	Send(SOCKET, String.c_str(),Length, ...);
}
void StoreString(std::string &String, ... /* All other data that you need to use Recv */)
{
	char Buffer[512];
	Recv(SOCKET, Buffer, 512, ...);
	String = Buffer;
}
Last edited on
could you also send 2d array of strings?
You mean a std::vector of std::strings? yes.
1
2
3
SendInt((int)vec.size());
for(int i=0;i<vec.size();i++)
    SendString(vec[i].c_str());

This first sends how many strings will be sent, then sends them all one by one... You will have to put Send(...) in place of SendInt and SendString.
closed account (2NywAqkS)
how would you receive a whole vector?
You first will receive a int representing the size of the vector.
Then you will have to store each vector element, one by one.
Like this:
Send:
1
2
3
4
5
Send(SOCKET,(unsigned int)YourVector.size(), sizeof(unsigned int), ...);
for(unsigned int i = 0; i < YourVector.size(); i++)
{
    Send(SOCKET,&(YourVector[i]),sizeof( /* Your Vector Type (Float, int, struct or whatever */ ), ...);
}

Recv:
1
2
3
4
5
6
7
8
9
10
int Size = 0;
Recv(SOCKET,&Size,sizeof(int),...);
for(int i = 0; i < Size; i++)
{
    Type t; // In case it's a Float vector, change Type to float
    // In case it's a Int vector, change Type to int
    // And so on...
    Recv(SOCKET,&t,sizeof(t),...);
    YourVector.push_back(t);
}
just noticed:
1
2
3
YourStruct a;
...
Send(SOCKET,&a,sizeof(a),...);


if you use that method do you still need to have the struct aligned?
and if you align it, if two values have different type but same bit length, which comes first?

and why does it have to be aligned?

and I've tried sending text(char*) but it seems to sepparate them at spaces...why does that happen?
Last edited on
If you use cin to get that string, it's cin who separates them. You should use cin.getline instead of cin << (char *).
About alignment:
I think you still need to align the data's...
I had a lot of troubles with data alignment, so I always try to align whatever I send.
I've read somewhere on cplusplus.com about alignment, and I hope those infos were true.
What you should know is: Always try to use chars instead of bools, in everything, even in every class you are going to put in another struct.
And never pass pointers in a struct. They will not get "converted" to an actual string, but Send will send it's Hex address instead.
About the order, if they have the same size, there is no problem with ordering them. There is a order i guess, i simply do not know what it is.
I don't know the reason, i just always got told to do that, lol.
Anyways, when you need to send some characters with a struct, you may want to do something like this:
1
2
3
4
5
6
7
8
9
struct NOT_VALID {
    char * Pointer; // This will be sent as a Memory Address, not as a Value!
};
struct VALID {
    char Text[512]; // You can send up to 512 characters like this
    // It will ALWAYS send 512 characters, indifferently if you write more or less
    // To copy a string, remember you can use strcpy or memcpy.
    // You may find useful to use strncpy because of its length-limits
};


I'm sorry but i cannot answer all of your question about alignment... I don't know those answers for myself either... Lol.
Oh, and if you use RakNet, there are a couple of functions that allows byte-alignment at runtime (Like, you make a unaligned struct, put it in a "BitStream" (A stream of data, you can put whatever you want in whatever size) in a "aligned" way (RakNet::BitStream::WriteAlignedX , where X is the Bit-alignment factor, and RakNet will fill the unaligned missing space with 0's i guess). There is even a string compression class. (For string, i mean char *'s).

And if you use Microsoft's Visual Studio, you can align structs like this:
1
2
3
#pragma pack(push, 1)
/* Define here your structs */
#pragma pack(pop) 


Have fun, and I hope all I wrote above is right and can help you!
I've tried sending a struct but here's the error:
 
recv(conn.ConnectSocket,&plrData,sizeof(plrData),0);


cannot convert parameter 2 from 'PLRDATA *' to 'char *'


I've noticed just now that you're using Recv(), maybe did you mean recv()?
Yes, i never use recv/send, but i know some of the theory.
About the answer, try like this:
 
recv(conn.ConnectSocket,(char *)&plrData,sizeof(plrData),0);

Eventually, after your headers, declare a macro like this:
1
2
3
4
#include <windows.h>
// ...
#define recv_s(Socket,DataPtr,Size,Other) recv( (Socket), (char *)(DataPtr), (Size), (Other))
#define send_s(Socket,DataPtr,Size,Other) send( (Socket), (char *)(DataPtr), (Size), (Other)) 

And use recv_s and send_s instead, without caring about using "(char *)" before "&(VarName)".
Last edited on
ok that did the trick, thx a lot!

now just to ask since I didn't get a proper answer in other topic and you're great with socket thingy:
how could I create a server that would accept more than one client?
Let's say I got like 5(could be any number though) clients that should be connected and be able to send their message to server and then receive it. Some kind of loop or is it solved another way?
You should create some threads:
Look up "beginthread", "beginthreadex" or CreateThread ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms682453(v=vs.85).aspx )
And, eventually store the "common data" in a global variable (like the last 15-20 chat messages or similar).
Each process should start from a new function you define by yourself:
So define a function that loops datacheck on a socket you can send by a user-pointer... This is the basic CreateThread function:
1
2
3
4
5
6
7
8
HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);

So you can use it like this:
 
HANDLE Thread = CreateThread(0, 0, YourFunction, &(YourSocket), 0, 0);

Where "YourFunction" is declared like this:
1
2
3
4
5
6
DWORD WINAPI /* if WINAPI does not work, try CALLBACK */ YourFunction(void * Data)
{
    SOCKET Socket = *((SOCKET *)Data); // This will copy the socket, so you will have less problems
    while(Connected) // Connected maybe is a Global Bool...
    Send/Recv...
}

Beware:
Do not do something like this at all:
1
2
3
4
5
if(ManyUsers)
{
    SOCKET YourSocket = LastSocket;
    CreateThread( /* ... */ );
}

Why? Because after CreateThread, YourSocket becomes invalid, and YourSocket's pointer has been sent to CreateThread. So, you can create a newed variable YourSocket, and change things like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
    // in your CreateThread...
    SOCKET * sk = new Socket;
    *sk = (YourSocket);
    CreateThread( 0, 0, YourFunction, sk, 0, 0); // Beware, sk is already a pointer, do not use "&sk", just "sk"
}
DWORD /* Whatever */ YourFunction(void * Socket)
{
    SOCKET * sock = (SOCKET *)Socket;
    while(Connected)
    {
        send( (*sock), ... );
    }
    delete sock;
}

You can wait until a process ends (Not suggested if you loop on a while) like this with WaitForSingleObject ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx ) :
1
2
3
4
{
    HANDLE Thread = CreateThread...;
    WaitForSingleObject(Thread, (TIME_TO_WAIT) );
}

TIME_TO_WAIT can be INFINITE (it's a macro).

You can create a new struct and send it to your "YourFunction" just like in a send/recv style. Just remember to use new and delete.

If you are too lazy, i suggest you again to use RakNet, it handles all of this for you.
Last edited on
Is threading only possible sollution if I'd like to do it all without any external tools?
I think so. After i've seen this 'threading' issue, i switched to RakNet. I really suggest you to try it, it has been developed for high-network-use projects.
Pages: 12