mirror of
https://github.com/beyondx/Notes.git
synced 2026-02-04 10:54:00 +08:00
Add New Notes
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-04T23:57:14+08:00
|
||||
|
||||
====== Can I send a C struct to a socket ======
|
||||
Created 星期六 04 六月 2011
|
||||
|
||||
I'm trying to send a struct to a DGRAM socket. Here's what i'm doing:
|
||||
|
||||
client (sends struct)
|
||||
|
||||
Code:
|
||||
|
||||
n = sendto(socket_fd,(const char*)&mypdu,sizeof(mypdu),0,
|
||||
(struct sockaddr *)&server_addr,sizeof(server_addr));
|
||||
|
||||
server
|
||||
|
||||
Code:
|
||||
|
||||
n=recvfrom(socket_fd,buffer,sizeof(mypdu),0,
|
||||
(struct sockaddr *)&from,&fromlen);
|
||||
|
||||
but in the client, i initialized one of the fields with 100, and in the server end, that field has the value 3 =\
|
||||
|
||||
Can I send the whole struct without sending it field by field?
|
||||
|
||||
|
||||
Re: Can I send a C struct to a socket?
|
||||
You will have to serialize the thing to send, and the receiver will need to reassemble the thing, very painfull.
|
||||
__________________
|
||||
|
||||
|
||||
Re: Can I send a C struct to a socket?
|
||||
You shouldn't send a whole struct at a time, in fact you shouldn't even send integers (int) "as-is".
|
||||
__I would memcopy the struct into a buffer and send the buffer __
|
||||
The problem with structs is that depending on the compiler, on the OS, on the architecture, ... there are different padding conventions that apply.
|
||||
|
||||
|
||||
As an example:
|
||||
|
||||
Code:
|
||||
|
||||
struct Foo
|
||||
{
|
||||
char foo;
|
||||
int bar;
|
||||
};
|
||||
|
||||
sizeof(struct Foo) is not guaranteed to (and in real world will almost never) be equal to sizeof(char)+sizeof(int) due to those padding issues.
|
||||
|
||||
|
||||
Moreover, depending on the CPU architecture there are problems of endianness too (most or least significant byte first?).
|
||||
|
||||
|
||||
Re: Can I send a C struct to a socket?
|
||||
Listen to aks44.
|
||||
|
||||
I'll just add that, even if you use datatypes that you're sure all of your networked devices regard as the same bitsize (for example, I can't think of a current architecture that doesn't consider "char" to be 8 bits), same byte order (again, "char" and "unsigned char" relieve you of that worry), or padding, you absolutely, absolutely cannot send any "pointer" to another machine. For example, assume you have the following two struct definitions:
|
||||
|
||||
Code:
|
||||
|
||||
struct Something {
|
||||
char buf[8];
|
||||
}
|
||||
|
||||
struct SomethingPtr {
|
||||
struct Something *something;
|
||||
}
|
||||
|
||||
You can't do something like sending these two structs:
|
||||
|
||||
Code:
|
||||
|
||||
mySomething = {"text"};
|
||||
MySomethingPtr = {&mySomething};
|
||||
|
||||
The mySomething address on one machine is going to be completely different on the other machine. You have to do something people often call "flattening the struct". You replace that pointer field with something that indicates to the other machine that it needs to reinsert a pointer to the mySomething struct you previously sent over.
|
||||
|
||||
You may want to take a look at how Microsoft's COM "marshalls" data between processes. There are examples of flattening structs there.
|
||||
Last edited by j_g; November 15th, 2007 at 06:33 PM..
|
||||
|
||||
|
||||
Re: Can I send a C struct to a socket?
|
||||
Quote:
|
||||
Originally Posted by kknd View Post
|
||||
You will have to serialize the thing to send, and the receiver will need to reassemble the thing, very painfull.
|
||||
This is the correct way to do things. although I would not describe it as "painful". It's just another opportunity to code more.
|
||||
|
||||
Aks44 was also correct in that endianess (sp?) is a factor. If you are transferring data from an Intel architecture to a PPC (power PC) architecture, then you will need to accommodate that the bytes in the words are swapped. M$'s .NET and CORBA handle this automagically. You can also do it yourself with minor effort.
|
||||
|
||||
Back to the serialize/unserialize procedure... you will need to perform a__ deep-copy of your structure into a buffer__, and if applicable, indicate the size of the buffer and sizes of any variable-length fields within the same buffer.
|
||||
|
||||
Once this buffer is sent across the wire, the recipient should be able to parse through the received buffer to reconstruct the original data structure.
|
||||
|
||||
Re: Can I send a C struct to a socket?
|
||||
this code is to work between Linux ix86 systems, so I don't think I have that sort of problems. I'm glad to say that it was fairly painless. My structure has mostly char* fields, one int (a cast to (const char*) did the trick), and I created a function to convert a list to a string, so I can send it in a buffer.
|
||||
|
||||
It's nice to have so many good responses in a relative short amount of time, in a subject that has almost anything, if anything at all, to do with Ubuntu
|
||||
|
||||
Thanks
|
||||
__________________
|
||||
56
Zim/Programme/APUE/send_struct_through_socket/Endianness.txt
Normal file
56
Zim/Programme/APUE/send_struct_through_socket/Endianness.txt
Normal file
@@ -0,0 +1,56 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-05T00:18:26+08:00
|
||||
|
||||
====== Endianness ======
|
||||
Created 星期日 05 六月 2011
|
||||
|
||||
Hi everyone,
|
||||
|
||||
I'm fighting with socket programming now and I've encountered a problem, which I don't know how to solve in a portable way. The task is simple : I need to send the array of 16 bytes over the network, receive it in a client application and parse it. I know, there are functions like htonl, htons and so one to use with uint16 and uint32. But what should I do with the chunks of data greater than that?
|
||||
|
||||
Thank you.
|
||||
|
||||
--------
|
||||
you say an array of 16 bytes. That doesnt really help. Endianness only matters fro things larger than a byte
|
||||
|
||||
if its really raw bytes then just send them, you will receive them just the same
|
||||
|
||||
It its really a struct you want to send it
|
||||
|
||||
struct msg
|
||||
{
|
||||
int foo;
|
||||
int bar;
|
||||
.....
|
||||
|
||||
Then you need to work through the buffer pulling that values you want.
|
||||
|
||||
When you send you must assemble a packet into a standard order
|
||||
|
||||
int off = 0;
|
||||
*(int*)&buff[off] = htonl(foo);
|
||||
off += sizeof(int);
|
||||
*(int*)&buff[off] = htonl(bar);
|
||||
...
|
||||
|
||||
when u receive
|
||||
|
||||
int foo = ntohl((int)buff[off]);
|
||||
off += sizeof(int);
|
||||
int bar = ntohl((int)buff[off]);
|
||||
....
|
||||
|
||||
edit : I see you want to send an IPv6 address, they are always in network byte order - so you can just stream it raw
|
||||
-----------------
|
||||
Endianness is a property of multibyte variables such as 16- and 32-but integers; it has to do with whether the high-order or low-order byte goes first. If the client application is processing the array as individual bytes, it doesn't have to worry about endianness, as the order of the bits within the bytes is the same.
|
||||
|
||||
----------------
|
||||
htons, htonl, etc., are for dealing with a single data item (e.g. an int) that's larger than one byte. An array of bytes where each one is used as a single data item itself (e.g., a string) doesn't need to be translated between host and network byte order at all.
|
||||
|
||||
------------------
|
||||
Bytes themselves don't have endianness any more in that any single byte transmitted by a computer will have the same value in a different receiving computer. Endianness only has relevance these days to multibyte data types such as ints.
|
||||
|
||||
In your particular case it boils down to knowing what the receiver will do with your 16 bytes. If it will treat each of the 16 entries in the array as discrete single byte values then you can just send them without worrying about endiannes. If, on the other hand, the receiver will treat your 16 byte array as four 32 bit integers then you'll need to run each integer through hton() prior to sending.
|
||||
|
||||
Does that help?
|
||||
@@ -0,0 +1,40 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-05T00:09:05+08:00
|
||||
|
||||
====== How to send an integer through a socket ======
|
||||
Created 星期日 05 六月 2011
|
||||
|
||||
I am trying to send an integer through a socket. I am using this code to do so; however, my C code will not compile. The compiler complains that myInt has not been declared.
|
||||
|
||||
int tmp = htonl(myInt);
|
||||
write(socket, &tmp, sizeof(tmp));
|
||||
|
||||
How do I declare myInt? Thanks.
|
||||
|
||||
------------
|
||||
Are you sure that it was properly declared in your program ?
|
||||
|
||||
Try like this:
|
||||
|
||||
int myInt = something;
|
||||
int tmp = htonl((uint32_t)myInt);
|
||||
write(socket, &tmp, sizeof(tmp));
|
||||
|
||||
-------------
|
||||
One simple solution is __typecase the integer to char and send 4bytes of the char buffer__
|
||||
|
||||
int myInt char * ptr = &myInt; write(socket, ptr, sizeof(int));
|
||||
|
||||
at the recieving end read the 4bytes.. u wont have any problem with the endianess.
|
||||
link|edit|flag
|
||||
|
||||
answered May 1 at 20:17
|
||||
maheshgupta024
|
||||
824
|
||||
|
||||
1
|
||||
|
||||
You certainly will have endian-ness problems if the two hosts are of different endian-ness. – Andrew Medico May 1 at 20:20
|
||||
|
||||
@maheshgupta024 - I think what you meant was to convert the integer to a textual representation (Such as you would see in XML or JSON). The problem is what you just posted ... doesn't do that. You'd need to use sprintf() for example to do the conversion. – Brian Roach May 1 at 20:36
|
||||
@@ -0,0 +1,72 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-05T16:11:43+08:00
|
||||
|
||||
====== Send a struct over a socket with correct padding and endianness in C ======
|
||||
Created 星期日 05 六月 2011
|
||||
|
||||
I have several structures defined to send over different Operating Systems (tcp networks). Defined structures are:
|
||||
|
||||
struct Struct1 { uint32_t num; char str[10]; char str2[10];}
|
||||
struct Struct2 { uint16_t num; char str[10];}
|
||||
|
||||
typedef Struct1 a;
|
||||
typedef Struct2 b;
|
||||
|
||||
The data is stored in a text file. Data Format is as such:
|
||||
|
||||
123
|
||||
Pie
|
||||
Crust
|
||||
|
||||
Struct1 a is stored as 3 separate parameters. However, struct2 is two separate parameters with both 2nd and 3rd line stored to the char str[] . The problem is when I write to a server over the multiple networks, the data is not received correctly. There are numerous spaces that separate the different parameters in the structures. How do I ensure proper sending and padding when I write to server? How do I store the data correctly (dynamic buffer or fixed buffer)?
|
||||
|
||||
Example of write: write(fd,&a, sizeof(typedef struct a)); Is this correct?
|
||||
|
||||
Problem Receive Side Output for struct2:
|
||||
|
||||
123( , )
|
||||
0 (, Pie)
|
||||
0 (Crust,)
|
||||
|
||||
Correct Output
|
||||
|
||||
123(Pie, Crust)
|
||||
------------------------
|
||||
write(fd,&a, sizeof(a)); is not correct; at least not portably, since the C compiler may introduce padding between the elements to ensure correct alignment. sizeof(typedef struct a) doesn't even make sense.
|
||||
|
||||
How you should send the data depends on the specs of your protocol. In particular, protocols define widely varying ways of sending strings. It is generally safest to send the struct members separately; either by multiple calls to write or writev(2). For instance, to send
|
||||
|
||||
struct { uint32_t a; uint16_t b; } foo;
|
||||
|
||||
over the network, where foo.a and foo.b already have the correct endianness, you would do something like:
|
||||
|
||||
struct iovec v[2];
|
||||
v[0].iov_base = &foo.a;
|
||||
v[0].iov_len = sizeof(uint32_t);
|
||||
v[1].iov_base = &foo.b;
|
||||
v[1].iov_len = sizeof(uint16_t);
|
||||
writev(fp, v, 2);
|
||||
|
||||
------------------
|
||||
1 down vote
|
||||
|
||||
|
||||
Sending structures over the network is tricky. The following problems you might have
|
||||
|
||||
Byte endiannes issues with integers.
|
||||
Padding introduced by your compiler.
|
||||
String parsing (i.e. detecting string boundaries).
|
||||
|
||||
If performance is not your goal, I'd suggest to create encoders and decoders for each struct to be send and received (ASN.1, XML or custom). If performance is really required you can still use structures and solve (1), by fixing an endianness (i.e. network byte order) and ensure your integers are stored as such in those structures, and (2) by fixing a compiler and using the pragmas or attributes to enforce a "packed" structure.
|
||||
|
||||
Gcc for example uses attribute((packed)) as such:
|
||||
|
||||
struct mystruct {
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
unsigned char text[24];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
(3) is not easy to solve. Using null terminated strings at a network protocol and depending on them being present would make your code vulnerable to several attacks. If strings need to be involved I'd use an proper encoding method such as the ones suggested above.
|
||||
----------------------
|
||||
@@ -0,0 +1,54 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-04T23:02:43+08:00
|
||||
|
||||
====== Sending Structured Data ======
|
||||
Created 星期六 04 六月 2011
|
||||
Hey,
|
||||
I thought it was incredibly cool that I could send in a structure over using send(2) and not have to parse a single string in both my client and server. So I thought I'd share, as I saw someone was using strings and was asking about "\r\n", I'm sure it's not hard to figure out as we were sending over integers in our labs anyway.
|
||||
|
||||
Quote:
|
||||
|
||||
// Please note that I just wrote this here, it's not a part of my actual code – you may use this idea:
|
||||
|
||||
typedef struct Data {
|
||||
char message[140];
|
||||
int type;
|
||||
} Data;
|
||||
|
||||
/* You can use send(2) to send over a copy of your structure
|
||||
and receive it using recv(2): */
|
||||
|
||||
// send:
|
||||
Data data;
|
||||
sprintf(data.message, "Hello, I'm Nima");
|
||||
data.type = 0x01;
|
||||
|
||||
int sent = send(sock, &data, sizeof(Data), 0);
|
||||
|
||||
// recv:
|
||||
|
||||
Data data;
|
||||
memset(&data, 0, sizeof(Data));
|
||||
int recved = recv(sock, &data, sizeof(Data), 0);
|
||||
|
||||
I love C.
|
||||
Back to top
|
||||
|
||||
« Last Edit: Dec 8th, 2010, 3:34am by Nimsical »
|
||||
WWW IP Logged
|
||||
reid
|
||||
Global Moderator
|
||||
Instructor
|
||||
*****
|
||||
|
||||
|
||||
|
||||
|
||||
Posts: 933
|
||||
|
||||
Show the link to this post Re: Sending Structured Data
|
||||
Reply #1 - Dec 8th, 2010, 9:49am
|
||||
Nice.
|
||||
|
||||
The problem with sending structured data is that you need to be careful about byte ordering. This will work fine as long as the two machines have the same byte order. I think it will work more generally if you convert the int to network byte order and back.
|
||||
@@ -0,0 +1,69 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-05T15:55:37+08:00
|
||||
|
||||
====== Sending struct over TCP (C-programming) ======
|
||||
Created 星期日 05 六月 2011
|
||||
|
||||
Hi again!
|
||||
|
||||
I have a client and server program where I want to send an entire struct from the client and then output the struct member "ID" on the server.
|
||||
|
||||
I have done all the connecting etc and already managed to send a string through:
|
||||
|
||||
send(socket, string, string_size, 0);
|
||||
|
||||
So, is it possible to send a struct instead of a string through send()? Can I just replace my buffer on the server to be an empty struct of the same type and go?
|
||||
|
||||
-----------------
|
||||
Welll... sending structs through the network is kinda hard if you are doing it properly.
|
||||
|
||||
Carl is right - you can send a struct through a network by saying:
|
||||
|
||||
send(socket, (char*)my_struct, sizeof(my_struct), 0);
|
||||
|
||||
But here's the thing:
|
||||
|
||||
sizeof(my_struct) might change between the client and server. Compilers often do some amount of padding and alignment, so unless you define alignment explicitly (maybe using a #pragma pack()), that size may be different.
|
||||
The other problem tends to be byte-order. Some machines are big-endian and others are little-endian, so the arrangement of the bytes might be different. In reality, unless your server or client is running on non-Intel hardware (which is probably not the case) then this problem exists more in theory than in practice.
|
||||
|
||||
So the solution people often propose is to have a routine that **serializes the struct**. That is, it __sends the struct one data member at a time__, ensuring that the client and server only send() and recv() the exact specified number of bytes that you code into your program.
|
||||
|
||||
-------------
|
||||
Serialization
|
||||
|
||||
It is often necessary to send or receive complex datastructures to or from another program that may run on a different architecture or may have been designed for different version of the datastructures in question. A typical example is a program that saves its state to a file on exit and then reads it back when started.
|
||||
|
||||
The 'send' function will typically start by writing a magic identifier and version to the file or network socket and then proceed to write all the data members one by one (i.e. in serial). If variable length arrays are encountered (e.g. strings), it will either write a length followed by the data or it will write the data followed by a special terminator. The format is often XML or binary in which case the htonl() set of macros may come in handy.
|
||||
|
||||
The 'receive' function will be nearly identical : It will read all the items on by one. Variable length arrays are either handled by reading the count followed by the data, or by reading the data until the special terminator is reached.
|
||||
|
||||
Since these two functions often follow the same pattern as the declaration of the data(structures), it would be nice if they could all be generated from a common definition.
|
||||
----------
|
||||
Not that it matters very much, but you should probably cast the structure to a void*, rather than a char *, since that's what send() is prototyped to take. – Mark Bessey Nov 14 '09 at 17:51
|
||||
|
||||
@rasher: While you mention both packing the struct and serializing, it's important to point out that serializing can murder performance if you're sending a high volume of data. Every call to send causes a fairly expensive context switch between user space and kernel space. __Packing the data is really the preferred method__.
|
||||
------------------
|
||||
Are the client and server machines "the same"? What you are proposing will only work if the the C compilers at each end lay out the structure in memory exactly the same. There are lots of reasons why this may not be the case. For example the client and server macines might have different architectures, then the way they represent numbers in memory (big-endian, little-endian) might differ. Even if the clients machines and server machine have the same architecture two different C compilers may have different policies for how they lay out structs in memory (eg. padding between fields to align ints on word boundaries). Even the same conmpiler with different flags might give different results.
|
||||
|
||||
Pragmatically, I'm guessing that your client and server are the same kind of machine and so what you are proposing will work, however you need to be aware that as a general rule it won't and that's why standards such as CORBA were invented, or why folks use some general representation such as XML
|
||||
---------------
|
||||
You can, if the client and the server have laid out the struct exactly the same way, meaning that the fields are all the same size, with the same padding. For instance if you have a long in your struct, that might be 32bits on one machine and 64bits on the other, in which case the struct will not be received correctly.
|
||||
|
||||
In your case, if the client and the server are always going to be on very similar C implementations (for instance if this is just code you're using to learn some basic concepts, or if for some other reason you know your code will only ever have to run on your current version of OSX), then you can probably get away with it. Just remember that your code will not necessarily work properly on other platforms, and that there's more work to do before it's suitable for use in most real-world situations.
|
||||
|
||||
For most client-server applications, this means that the answer is you can't do it in general. What you actually do is define the message in terms of the number of bytes sent, what order, what they mean, and so on. Then at each end, you do something platform-specific to ensure that the struct you're using has exactly the required layout. Even so, you might have to do some byte-swapping if you're sending integer members of the struct little-endian, and then you want your code to run on a big-endian machine. Data interchange formats like XML, json and Google's protocol buffers exist so that you don't have to do this fiddly stuff.
|
||||
|
||||
[Edit: also remember of course that some struct members can never be sent over the wire. For instance if your struct has a pointer in it, then the address refers to memory on the sending machine, and is useless on the receiving end. Apologies if this is already obvious to you, but it certainly isn't obvious to everyone when they're just beginning with C].
|
||||
|
||||
-------------
|
||||
Technically, the struct will always be received correctly assuming his protocol over the connection works correctly. The problem is not the receiving, it is the interpreting or perhaps you might call it the accessing.
|
||||
------------
|
||||
True. I'd say if you don't actually read all the bytes into application space (because sizeof(thestruct) is smaller on the reader than the writer), then you haven't received the message. Also, I think technically it is sometimes undefined behavior to copy arbitrary bytes over a struct - you could trigger trap bits in the members. So in that case too the message is not received. But you're right, you'll receive (some of) the bytes of the message, just not understand the message itself.
|
||||
------------------
|
||||
In general, it's a bad idea to do this, even if your client and your server turn out to lay out the structure in memory the same way.
|
||||
|
||||
Even if you don't intend to pass more complex data structures (that would involve pointers) back and forth, I recommend that you serialize your data before you send it over the network.
|
||||
----------------------
|
||||
Send the data as text file and then decode it after receiving it. This is the best way if you want your data as its sent!!
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-05T16:07:02+08:00
|
||||
|
||||
====== structure padding and structure packing ======
|
||||
Created 星期日 05 六月 2011
|
||||
|
||||
struct mystruct_A
|
||||
{
|
||||
char a;
|
||||
int b;
|
||||
char c;
|
||||
}x;
|
||||
|
||||
struct mystruct_B
|
||||
{
|
||||
int b;
|
||||
char a;
|
||||
}y;
|
||||
|
||||
the size of structure is 12 and 8 respectively...is this structure padding or packing...can any one tell me when padding takes and packing takes...is this structure padded or packing..??
|
||||
|
||||
------------
|
||||
Padding aligns structure members to "natural" address boundaries - say, int members would have offsets, which are mod(4) == 0 on 32-bit platform. Padding is on by default. It inserts the following "gaps" into your first structure:
|
||||
|
||||
struct mystruct_A {
|
||||
char a;
|
||||
char gap_0[3]; /* inserted by compiler: for alignment of b */
|
||||
int b;
|
||||
char c;
|
||||
char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
|
||||
} x;
|
||||
|
||||
Packing, on the other hand prevents compiler from doing padding - this has to be explicitly requested - under GCC it's __attribute__((__packed__)), so the following:
|
||||
|
||||
struct __attribute__((__packed__)) mystruct_A {
|
||||
char a;
|
||||
int b;
|
||||
char c;
|
||||
};
|
||||
|
||||
would produce structure of size 6 on a 32-bit architecture.
|
||||
|
||||
A note though - unaligned memory access is slower on architectures that allow it (like x86 and amd64), and is explicitly prohibited on strict alignment architectures like SPARC.
|
||||
@@ -0,0 +1,49 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-05T15:14:03+08:00
|
||||
|
||||
====== Sending variable size arrays from client to server ======
|
||||
Created 星期日 05 六月 2011
|
||||
I'm in a peculiar situation, maybe somebody could offer some advice.
|
||||
|
||||
I'm using ENet as my main mode of client/server communications at the moment, and have been able to successfully send basic structs back and forth for quite some time.
|
||||
|
||||
I am now in the position, however, in which I need to send variable-sized arrays back and forth between these two machines. I'd like to store this information in a struct, as sending and receiving structs has been pretty easy so far. Sending structs with variable-sized arrays in them appears to be a lot different, though.
|
||||
|
||||
Do I need to use some kind of object serialization or is there a simpler way to do this that I haven't discovered yet?
|
||||
|
||||
The basic idea of the struct I need to send is as follows:
|
||||
|
||||
typedef struct _StructName
|
||||
{
|
||||
int arr_size;
|
||||
int arr[];
|
||||
} StructName;
|
||||
|
||||
|
||||
|
||||
Thanks in advance for any help or suggestions.
|
||||
-----------------
|
||||
Structs are fixed-size, and you want to send a variable sized object, so no, you can't do it that way.
|
||||
|
||||
The typical solution is simply this:
|
||||
|
||||
Sender: send number of elements, then each element in turn.
|
||||
Receiver: read number of elements, then read that many elements in turn.
|
||||
|
||||
I'm assuming you already have a method for determining which object type you're about to receive.
|
||||
----------------------
|
||||
Yes, you need to serialize your array somehow. A prefix length followed by the data is common. (You don't need to go full stateless object network serialization, though, just serializing this particular type would be fine)
|
||||
-------------------------
|
||||
Rather than being terribly specific at this hour, I thought I'd give a handful of random advice --
|
||||
|
||||
Firstly, be aware of struct padding and packing; become familiar with your compiler's "packing" directive. Using Microsoft's compiler, I believe it's goes something like #pragma pack(push) #pragma pack(1) <struct definitions> <pragma pack(pop). Eventually you'll have to implement proper serialization for versioning and portability, but until then you can at least avoid sending padding bytes. Also, order your data from the largest datatype down in your structs (and be aware that is the order the variables are initialized in if you have constructors with parameter lists. Keep in mind that such "unnatural" alignment makes the structs less performant to access, so it's probably worth having a packed version for network transmission, and an unpacked version to perform calculations against.
|
||||
|
||||
When you do get to serialization, you can send data very efficiently -- say, packing a value of 1-50 in 6 bits, or a Boolean value in just 1. Or maybe by compressing the message payload. It takes some work to extract, but in general you're going to expect to get about 10 full network updates over the WWW per second, and the clients have what is effectively forever to decode messages.
|
||||
|
||||
The Decorator pattern is very useful in building up packets or file IO, look into it.
|
||||
|
||||
Message size is somewhat of a trade-off against latency -- that is, the longer you spend waiting for enough data to fill a message to the brim, its that much longer the receiver has to wait for fresh data. It may be worth implementing some kind of timer that will cut off the current message if its been waiting around too long.
|
||||
|
||||
Most systems use two primary streams, on UDP-based for fast-paced game data, and a second TCP-based for data which is not time-sensitive, such as chat text, score updates, etc.
|
||||
|
||||
155
Zim/Programme/APUE/send_struct_through_socket/pragma.txt
Normal file
155
Zim/Programme/APUE/send_struct_through_socket/pragma.txt
Normal file
@@ -0,0 +1,155 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-05T12:32:12+08:00
|
||||
|
||||
====== pragma ======
|
||||
Created 星期日 05 六月 2011
|
||||
#pragma
|
||||
百科名片
|
||||
|
||||
在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
|
||||
|
||||
目录
|
||||
|
||||
一般格式
|
||||
常用参数
|
||||
应用实例
|
||||
展开
|
||||
|
||||
编辑本段一般格式
|
||||
其格式一般为: #Pragma Para。其中Para 为参数,下面来看一些常用的参数
|
||||
编辑本段常用参数
|
||||
|
||||
[#pragma]
|
||||
|
||||
#pragma
|
||||
message 参数
|
||||
Message 参数能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
|
||||
#Pragma message(“消息文本”)
|
||||
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
|
||||
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
|
||||
#ifdef _X86
|
||||
#Pragma message(“_X86 macro activated!”)
|
||||
#endif
|
||||
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。
|
||||
code_seg
|
||||
另一个使用得比较多的pragma参数是code_seg。格式如:
|
||||
#pragma code_seg( ["section-name"[,"section-class"] ] )
|
||||
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。
|
||||
#pragma once
|
||||
(比较常用)
|
||||
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。
|
||||
#pragma once是编译相关,就是说这个编译系统上能用,但在其他编译系统不一定可以,也就是说移植性差,不过现在基本上已经是每个编译器都有这个定义了。
|
||||
#ifndef,#define,#endif这个是C++语言相关,这是C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的,如果写的程序要跨平台,最好使用这种方式
|
||||
#pragma hdrstop
|
||||
#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。
|
||||
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。
|
||||
#pragma resource
|
||||
#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。
|
||||
#pragma warning
|
||||
#pragma warning( disable : 4507 34; once : 4385; error : 164 )
|
||||
等价于:
|
||||
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
|
||||
#pragma warning(once:4385) // 4385号警告信息仅报告一次
|
||||
#pragma warning(error:164) // 把164号警告信息作为一个错误。
|
||||
同时这个pragma warning 也支持如下格式:
|
||||
#pragma warning( push [ ,n ] )
|
||||
#pragma warning( pop )
|
||||
这里n代表一个警告等级(1---4)。
|
||||
#pragma warning( push )保存所有警告信息的现有的警告状态。
|
||||
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。
|
||||
#pragma warning( pop )向栈中弹出最后一个警告信息,
|
||||
在入栈和出栈之间所作的一切改动取消。例如:
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 4705 )
|
||||
#pragma warning( disable : 4706 )
|
||||
#pragma warning( disable : 4707 )
|
||||
//.......
|
||||
#pragma warning( pop )
|
||||
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
|
||||
pragma comment
|
||||
pragma comment(...)
|
||||
该指令将一个注释记录放入一个对象文件或可执行文件中。
|
||||
常用的lib关键字,可以帮我们连入一个库文件。
|
||||
每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。例如,对循环优化功能:
|
||||
#pragma loop_opt(on) // 激活
|
||||
#pragma loop_opt(off) // 终止
|
||||
有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,如“Parameter xxx is never used in function xxx”,可以这样:
|
||||
#pragma warn —100 // Turn off the warning message for warning #100
|
||||
int insert_record(REC *r)
|
||||
{ /* function body */ }
|
||||
#pragma warn +100 // Turn the warning message for warning #100 back on
|
||||
函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。
|
||||
每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。
|
||||
#pragma pack(n)和#pragma pack()
|
||||
struct sample
|
||||
{
|
||||
char a;
|
||||
double b;
|
||||
};
|
||||
当sample结构没有加#pragma pack(n)的时候,sample按最大的成员那个对齐;
|
||||
(所谓的对齐是指对齐数为n时,对每个成员进行对齐,既如果成员a的大小小于n则将a扩大到n个大小;
|
||||
如果a的大小大于n则使用a的大小;)所以上面那个结构的大小为16字节.
|
||||
当sample结构加#pragma pack(1)的时候,sizeof(sample)=9字节;无空字节。
|
||||
(另注:当n大于sample结构的最大成员的大小时,n取最大成员的大小。
|
||||
所以当n越大时,结构的速度越快,大小越大;反之则)
|
||||
#pragma pack()就是取消#pragma pack(n)的意思了,也就是说接下来的结构不用#pragma pack(n)
|
||||
#pragma comment( comment-type ,["commentstring"] )
|
||||
comment-type是一个预定义的标识符,指定注释的类型,应该是compiler,exestr,lib,linker之一。
|
||||
commentstring是一个提供为comment-type提供附加信息的字符串。
|
||||
注释类型:
|
||||
1、compiler:
|
||||
放置编译器的版本或者名字到一个对象文件,该选项是被linker忽略的。
|
||||
2、exestr:
|
||||
在以后的版本将被取消。
|
||||
3、lib:
|
||||
放置一个库搜索记录到对象文件中,这个类型应该是和commentstring(指定你要Linker搜索的lib的名称和路径)这个库的名字放在Object文件的默认库搜索记录的后面,linker搜索这个这个库就像你在命令行输入这个命令一样。你可以在一个源文件中设置多个库记录,它们在object文件中的顺序和在源文件中的顺序一样。如果默认库和附加库的次序是需要区别的,使用Z编译开关是防止默认库放到object模块。
|
||||
4、linker:
|
||||
指定一个连接选项,这样就不用在命令行输入或者在开发环境中设置了。
|
||||
只有下面的linker选项能被传给Linker.
|
||||
/DEFAULTLIB ,/EXPORT,/INCLUDE,/MANIFESTDEPENDENCY, /MERGE,/SECTION
|
||||
(1) /DEFAULTLIB:library
|
||||
/DEFAULTLIB 选项将一个 library 添加到 LINK 在解析引用时搜索的库列表。用 /DEFAULTLIB指定的库在命令行上指定的库之后和 .obj 文件中指定的默认库之前被搜索。忽略所有默认库 (/NODEFAULTLIB) 选项重写 /DEFAULTLIB:library。如果在两者中指定了相同的 library 名称,忽略库 (/NODEFAULTLIB:library) 选项将重写 /DEFAULTLIB:library。
|
||||
(2)/EXPORT:entryname[,@ordinal[,NONAME]][,DATA]
|
||||
使用该选项,可以从程序导出函数,以便其他程序可以调用该函数。也可以导出数据。通常在 DLL 中定义导出。entryname 是调用程序要使用的函数或数据项的名称。ordinal 在导出表中指定范围在 1 至 65,535 的索引;如果没有指定 ordinal,则 LINK 将分配一个。NONAME 关键字只将函数导出为序号,没有 entryname。
|
||||
DATA 关键字指定导出项为数据项。客户程序中的数据项必须用 extern __declspec(dllimport) 来声明。
|
||||
有三种导出定义的方法,按照建议的使用顺序依次为:
|
||||
源代码中的 __declspec(dllexport).def 文件中的 EXPORTS 语句LINK 命令中的 /EXPORT 规范所有这三种方法可以用在同一个程序中。LINK 在生成包含导出的程序时还创建导入库,除非生成中使用了 .exp 文件。
|
||||
LINK 使用标识符的修饰形式。编译器在创建 .obj 文件时修饰标识符。如果 entryname 以其未修饰的形式指定给链接器(与其在源代码中一样),则 LINK 将试图匹配该名称。如果无法找到唯一的匹配名称,则 LINK 发出错误信息。当需要将标识符指定给链接器时,请使用 Dumpbin 工具获取该标识符的修饰名形式。
|
||||
(3)/INCLUDE:symbol
|
||||
/INCLUDE 选项通知链接器将指定的符号添加到符号表。
|
||||
若要指定多个符号,请在符号名称之间键入逗号 (,)、分号 (;) 或空格。在命令行上,对每个符号指定一次 /INCLUDE:symbol。
|
||||
链接器通过将包含符号定义的对象添加到程序来解析 symbol。该功能对于添包含不会链接到程序的库对象非常有用。用该选项指定符号将通过 /OPT:REF 重写该符号的移除。
|
||||
我们经常用到的是#pragma comment(lib,"*.lib")这类的。#pragma comment(lib,"Ws2_32.lib")表示链接Ws2_32.lib这个库。 和在工程设置里写上链入Ws2_32.lib的效果一样,不过这种方法写的 程序别人在使用你的代码的时候就不用再设置工程settings了
|
||||
编辑本段应用实例
|
||||
在网络协议编程中,经常会处理不同协议的数据报文。一种方法是通过指针偏移的
|
||||
方法来得到各种信息,但这样做不仅编程复杂,而且一旦协议有变化,程序修改起来
|
||||
也比较麻烦。在了解了编译器对结构空间的分配原则之后,我们完全可以利用这
|
||||
一特性定义自己的协议结构,通过访问结构的成员来获取各种信息。这样做,
|
||||
不仅简化了编程,而且即使协议发生变化,我们也只需修改协议结构的定义即可,
|
||||
其它程序无需修改,省时省力。下面以TCP协议首部为例,说明如何定义协议结构。
|
||||
其协议结构定义如下:
|
||||
#pragma pack(1) // 按照1字节方式进行对齐
|
||||
struct TCPHEADER
|
||||
{
|
||||
short SrcPort; // 16位源端口号
|
||||
short DstPort; // 16位目的端口号
|
||||
int SerialNo; // 32位序列号
|
||||
int AckNo; // 32位确认号
|
||||
unsigned char HaderLen : 4; // 4位首部长度
|
||||
unsigned char Reserved1 : 4; // 保留6位中的4位
|
||||
unsigned char Reserved2 : 2; // 保留6位中的2位
|
||||
unsigned char URG : 1;
|
||||
unsigned char ACK : 1;
|
||||
unsigned char PSH : 1;
|
||||
unsigned char RST : 1;
|
||||
unsigned char SYN : 1;
|
||||
unsigned char FIN : 1;
|
||||
short WindowSize; // 16位窗口大小
|
||||
short TcpChkSum; // 16位TCP检验和
|
||||
short UrgentPointer; // 16位紧急指针
|
||||
};
|
||||
#pragma pack() // 取消1字节对齐方式 #pragma pack规定的对齐长度,实际使用的规则是: 结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。 也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。 而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。
|
||||
指定连接要使用的库比如我们连接的时候用到了 WSock32.lib,你当然可以不辞辛苦地把它加入到你的工程中。但是我觉得更方便的方法是使用 #pragma 指示符,指定要连接的库:#pragma comment(lib, "WSock32.lib")
|
||||
|
||||
扩展阅读:
|
||||
@@ -0,0 +1,71 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2011-06-04T17:18:13+08:00
|
||||
|
||||
====== send structure using socket ======
|
||||
Created 星期六 04 六月 2011
|
||||
|
||||
------The problem with sending binary data structures over the network is manifold:
|
||||
|
||||
1. You don't know if the structure is compatible with the hardware/software on the other end (word sizes, structure padding/alignment, word endianess, etc).
|
||||
2. Because of #1, you need to encode the structure to some network-neutral format when it's sent and decode it on the receiving end.
|
||||
3. There are many means to accomplish #2, including XML (verbose, but human-readable).
|
||||
|
||||
You can eliminate this cruft if you are 100% certain that the sending and receiving ends are 100% binary compatible and that the applications were built on both ends with the same compiler and linker settings. Good luck in that!
|
||||
|
||||
-------What you should do is serialize the information in the structure into a generic format, send that over the network, and recreate the object on the other side.
|
||||
|
||||
Trying to do the equivalent of a memcpy of the struct over a socket will probably fail horribly.
|
||||
|
||||
I suggest serializing to XML. There's lots of libraries available to work with it.
|
||||
|
||||
------------Linux程序设计 Linux socket send and recevie structure
|
||||
|
||||
最近在开发一个Linux下的聊天软件,好久没有做C语言的开发了,感觉到很多东西已经生疏了,这下又碰到用Socket传递结构体的问题,google了一下,发现也有不少朋友遇到同样的问题,所以就打算写出自己的解决办法,跟大家分享。
|
||||
Socket中的send函数可以发送字符串,但不能直接发送结构体,因此在发送端先把结构体转成字符串,然后用send发送,在接收端recv字符串,再转换成原先的结构体,这个就是解决问题的主要思路,实现中要注意的问题在下文阐述。
|
||||
为了客户端之间能够互相通信,实现私聊,我采用服务器转发的方式,因此用户发送的每条消息中除了消息主体外,还必须包含有发送者、接收者ID等信息,如此采用结构体便是最佳的办法了。我定义的结构体如下:
|
||||
|
||||
struct send_info
|
||||
{
|
||||
char info_from[20]; //发送者ID
|
||||
char info_to[20]; //接收者ID
|
||||
int info_length; //发送的消息主体的长度
|
||||
char info_content[1024]; //消息主体
|
||||
};
|
||||
|
||||
发送端主要代码(为了简洁说明问题,我把用户输入的内容、长度等验证的代码去掉了):
|
||||
|
||||
struct send_info info1; //定义结构体变量
|
||||
printf("This is client,please input message:");
|
||||
//从键盘读取用户输入的数据,并写入info1.info_content
|
||||
memset(info1.info_content,0,sizeof(info1.info_content));//清空缓存
|
||||
info1.info_length=read(STDIN_FILENO,info1.info_content,1024) - 1;//读取用户输入的数据
|
||||
|
||||
memset(snd_buf,0,1024);//清空发送缓存,不清空的话可能导致接收时产生乱码,
|
||||
//或者如果本次发送的内容少于上次的话,snd_buf中会包含有上次的内容
|
||||
|
||||
__memcpy(snd_buf,&info1,sizeof(info1)); //结构体转换成字符串__
|
||||
send(connect_fd,snd_buf,sizeof(snd_buf),0);//发送信息
|
||||
|
||||
接收端主要代码:
|
||||
|
||||
struct send_info clt; //定义结构体变量
|
||||
|
||||
memset(recv_buf,'z',1024);//清空缓存
|
||||
recv(fd,recv_buf,1024,0 );//读取数据
|
||||
|
||||
memset(&clt,0,sizeof(clt));//清空结构体
|
||||
__memcpy(&clt,recv_buf,sizeof(clt));//把接收到的信息转换成结构体__
|
||||
|
||||
clt.info_content[clt.info_length]='';
|
||||
//消息内容结束,没有这句的话,可能导致消息乱码或输出异常
|
||||
//有网友建议说传递的结构体中尽量不要有string类型的字段,估计就是串尾符定位的问题
|
||||
|
||||
if(clt.info_content) //判断接收内容并输出
|
||||
printf("nclt.info_from is %snclt.info_to is %snclt.info_content is%snclt.info_length is %dn",clt.info_from,clt.info_to,clt.info_content,clt.info_length);
|
||||
//至此,结构体的发送与接收已经顺利结束了
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user