stream socket programming

23
Stream Socket Programming Idioms and pitfalls

Upload: janice

Post on 15-Jan-2016

25 views

Category:

Documents


0 download

DESCRIPTION

Stream Socket Programming. Idioms and pitfalls. Stream Socket Characteristics. Transmissions across a stream socket are considered to be a continuous stream of bytes. Any other structure must be created by the applications participating in the communication. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Stream Socket Programming

Stream Socket Programming

Idioms and pitfalls

Page 2: Stream Socket Programming

Stream Socket Characteristics

• Transmissions across a stream socket are considered to be a continuous stream of bytes.

• Any other structure must be created by the applications participating in the communication.

• "Message" boundaries are not guaranteed to be preserved.

• Most applications DO want to communicate in terms of a series of separate messages.

Page 3: Stream Socket Programming

Application Protocols

• Rules the processes involved in your application use to communicate.

• Includes:– Allowed message types (formats)– Rules about when each message type can be

sent.

• Programming language structures don't always map exactly onto your message formats

Page 4: Stream Socket Programming

Banking Example

• Report total deposit amount (in pennies), total withdrawal amount (in pennies), number of deposits, number of withdrawals.

• Design decision #1: encoding scheme– Character strings of digits– Binary integer values

Page 5: Stream Socket Programming

Character Encoding

• Advantages– No limit on size of values that can be encoded– No byte ordering issues

• Disadvantages– Inefficient– Easy to get buffer size wrong or waste space– Must be careful about delimiters

Page 6: Stream Socket Programming

Binary numeric encoding

• Advantages– Uses fewer bits to represent a given value– Fields in message are a fixed number of bytes

• Disadvantages– Byte ordering is significant – must use

hton/ntoh– Building structs to represent messages isn't

always straightforward (alignment issues)

Page 7: Stream Socket Programming

Alignment rules

• Compilers lay out structs to maximize alignment.• Fields are allocated in the order they are

declared• A data value is aligned if its address is a multiple

of its size (e.g. 32 bit ints – 4 bytes – at addresses divisible by 4)

• A struct is aligned if its address is a multiple of the size of the largest data type it contains.

• Unnamed padding bytes are added to keep struct members aligned.

Page 8: Stream Socket Programming

Example

struct mixedData {

char Data1;

short Data2;

int Data3;

char Data4;

};

Total data bytes = 1 + 2 + 4 + 1 = 8

Page 9: Stream Socket Programming

Examplestruct MixedData /* after compilation */{ char Data1; char Padding0[1]; /* So following 'short' is

on a 2 byte boundary */ short Data2; int Data3; char Data4; char Padding1[3];};

Total bytes = 1 + 1 + 2 + 4 + 1 + 3 = 12Size is a multiple of sizeof(int)

Page 10: Stream Socket Programming

To avoid padding:

struct mixedData2 { int Data3; short Data2; char Data1; char Data4;};

Data items declared in order of decreasing size. Assuming actual space needed is a multiple of size of largest data item, no padding needed.

sizeof(mixedData2) = 8

Page 11: Stream Socket Programming

Bank Example

struct bankMsg {

int depositAmt;

short depositCnt;

int withdrawAmt;

short withdrawCnt;

};• Total data size is 4 + 2 + 4 + 2 bytes = 12 bytes• On RHEL 5, using gcc, sizeof(bankMsg) = 16. Why?

Page 12: Stream Socket Programming

No padding needed:

struct bankMsg {int depositAmt;

int withdrawAmt;

short depositCnt;

short withdrawCnt;

};

You can also add the padding to your definition so it is explicit. (Recall sockaddr_in)

Page 13: Stream Socket Programming

Parsing received messages

• If the fields are fixed size, we can just send and receive the associated struct:struct bankMsg msg;void *buffer = (void *) &msg;msg.depositAmt = 2324234;msg.withdrawAmt = 2232344;msg.depositCnt = 50;msg.withdrawCnt = 42;

send(s, &msg, sizeof(bankMsg), 0);

Page 14: Stream Socket Programming

Parsing received messages• In the receiving process:

struct bankMsg msg;void *buffer = (void *) &msg;int rbytes, rv;...for (rbytes = 0; rbytes < sizeof(msg);

rbytes += rv){if ((rv = recv(s, buffer + rbytes,

sizeof(msg) – rbytes, 0)) < 0) /* handle error */

}/* Fix byte order! */msg.depositAmt = ntohl(msg.depositAmt);...

Page 15: Stream Socket Programming

Portability/Compatibility

• The C/C++ standards don't specify alignment rules – left up to compiler implementations

• Say you don't pay attention to padding when you define message format structs in your code. What can go wrong?

Page 16: Stream Socket Programming

Delimited char strings

• Sending multiple messages consisting of variable-length delimited character strings is tricky.

• Doing a receive may give you bytes from more than one message.

• Depending on how you have structured your parsing routines, it may be complicated to track which bytes of a receive buffer you have parsed.

Page 17: Stream Socket Programming

Delimited char strings

• One solution: for each delimited string you expect, receive one byte at a time until you receive the delimiter.

• Leaves subsequent strings waiting in the received stream for subsequent calls to recv().

• Downside: slower than multi-byte receives, but if you don't know how many characters to expect, you're kind of stuck.

Page 18: Stream Socket Programming

Working with core dumps

• When a program crashes, it can create a file containing the image of the address space of the process at the time of the crash – a "core dump"

• Debuggers can examine these files and show you precisely where the error occurred. Good for tracking down segmentation faults.

Page 19: Stream Socket Programming

Requirements

• You must compile your program with –g to preserve symbol table information for the debugger

• You need to be allowed to create core files in your account.

• Use the ulimit –c command to check. If return is 0, no core files will be created.

• Change with "ulimit –c unlimited"• Caution: you can only do this once per login

session, you can't switch back and forth.

Page 20: Stream Socket Programming

Using core dumps

• If a core dump is created, you will see "(core dumped)" in the error message

• Linux creates a file called core.n, where n is a unique number

• To examine the core file, use gdb (ddd also works):

gdb executable-name corefile-name

Page 21: Stream Socket Programming

What you see

• gdb reports where the error occurred, e.g.

#0 0x08048370 in a (p=0x0) at test_core.c:1111 int y = *p;

• a is the method name• p is the variable that caused the problem• test_core is the name of the executable, 11 is

the line number• you can use gdb to examine variable values,

etc.

Page 22: Stream Socket Programming

Caveats

• Core files are big, and because of the Linux naming convention, you will create a separate one every time a program crashes.

• Pay attention to creation dates, and make sure you're examining the latest core dump.

• Periodically delete core files. Make core.* one of the things the "clean" target in your makefile cleans up

• Don't set ulimit to "unlimited" unless you need to examine a core dump.

Page 23: Stream Socket Programming

A reasonable tutorial

• http://www.network-theory.co.uk/articles/gccdebug.html