Sockets (Part 2)
This page was last modified 2009-06-03 09:56:12 by Puchu.Net user Choco. (Show history)

Contents

Sockets, Continued

This is part 2 of the article regarding socket programming. Information about system calls is in Sockets (Part 1).

Client / Server

Program flow of client/server in terms of system calls can be visualized as:

Server Client
socket()
Bind to an IP address and port bind()
Queue up connection requests listen() socket()
Remove connection from queue accept() connect() Try to connect to server
recv()
recvfrom()
send()
sendto()
send()
sendto()
recv()
recvfrom()
shutdown() shutdown()
close() close()

Note that, on the server side accept() is blocking the process until a connection is made. So in order to handle multiple connections we can use the following concept:

listen(fd, 10);
do {
  client = accept(fd, &daddr, &daddr_len);
  pid = fork();
  if (pid == 0) {
    // this is the child process for handling new
    // socket descriptor 'client'
    close(client);
    exit(0);
  }
  else {
    close(client);
    // parent goes back to accepting connections
    // immediately
  }
} while(1);

Another technique to multiplex I/O is using poll() function, to which you pass a list of data structure containing the descriptor, whether you intend to read or write, and the function will tell you if the descriptor is ready for reading or writing. This function doesn't exist under Windows Sockets 2, but is implemented as a member function of System.Net.Socket class, so is an option if you are using .Net Framework.

Above diagram and code segment are based on work by Thomas Yu and Peter Burden; please see references below for original documents.

Discovery and Resolution

Find Peer

To find out who you are connected to:

int getpeername(int fd, struct sockaddr *addr, int *addr_len);

This function will provide the socket address information based on socket descriptor. Then the data structure can be used with:

char* inet_ntoa(struct sockaddr addr);

to resolve the name of an IP address.

Find Host Environment

Host environment information (name, aliases, IP address, etc.) is stored in this data structure:

struct hostent {
  char  *h_name;
  char **h_aliases;
  short  h_addrtype;
  short  h_length;
  char **h_addr_list; // contains the IP address
};

If you have the name of a machine, this function can be used to find its network information:

struct hostent* gethostbyname(const char *name); struct hostent* getipnodebyname(const char *name, int type, int flags, int *error);

or if you have the address instead, use these:

struct hostent* gethostbyaddr(const char *addr, int len, int type); struct hostent* getipnodebyaddr(const void *name, int type, int flags, int *error);

The functions gethostbyname() and gethostbyaddr() are legacy functions, but they exist in Windows Sockets 2 implementation.

Sockets for Windows

Include winsock2.h and most of the system calls have the same name; the only exception is closesocket() for close. See Winsock Functions for reference. Some of the constants are different between Linux and Windows:

Function Linux Windows
socket() PF_UNIX  
  PF_INET AF_INET
  PF_INET6 AF_INET6
    AF_IRDA
    AF_BTM
shutdown() SHUT_RD SD_RECEIVE
  SHUT_WR SD_SEND
  SHUT_RDWR SD_BOTH

When you get a SOCKET_ERROR return value, you can call WSAGetLastError() to find out why an error took place.

You can use System.Net.SocketAddress and System.Net.Sockets.Socket when working with .Net Framework. There are also classes you can use: TcpClient, TcpListener, and UdpClient.

Notes

If you are using TCP, then it is possible that the message you are sending is too small in relation to the send buffer, and the system will try to buffer it to send along with other data. This will work against short messages such as key stroke, etc. One way to work around this issue is to send message as out-of-band. Or alternatively, you can set the socket option TCP_NODELAY to disable the Nagle algorithm. You can find more information here and here.

When working with .Net Framework sockets, sometimes it'll appear that one of your thread is stuck on Socket.Send(). This may be related to the fact that sockets are blocking by default. You can find more information here, for some examples and analysis. You can make a socket non-blocking by setting the Blocking property:

sd.Blocking = false;

References

  • OSI Model document from Wikipedia has an excellent diagram outlining the function and data unit of each layer:
Data unit Layer Function
Host
layers
Data (7) Application Network process to application
(6) Presentation Data representation and encryption
(5) Session Interhost communication
Segments (4) Transport End-to-end connections and reliability (TCP)
Media
layers
Packets (3) Network Path determination and logical addressing (IP)
Frames (2) Data link Physical addressing (MAC and LLC)
Bits (1) Physical Media, signal and binary transmission
Number Protocol
0x1 ICMP
0x6 TCP
0x11 UDP
Puchu.Net

Document is accessible from http://www.puchu.net. © 2002-2010 Sean Yang, Karen Yang, Don Yang and/or respective authors, all rights reserverd.

This material may contain (biased) opinions, inappropriate materials for numerous individuals, work of other authors from the internet, links that refer to other web documents and resources, or origial work that cannot be use for personal or commercial purposes. Please respect the work of original authors.


Creative Commons License
Powered By MediaWiki

© 2002-2010 Sean Yang, all rights reserved.