Friday, January 25, 2008

[Programming] Quick guide for network programming (1)



When we talk about network programming, basically it includes two parts: one is the socket programming, another one is the multi-threaded programming.


I'll give a step-by-step guidance for network programming in my blog. We'll first talk about socket programming, then go to the multi-threaded programming. I'll use the windows system as a demonstration. A TCP server will be first started up, then a TCP cilent will communicate with the server from the loopback IP address 127.0.0.1 with port number 6123. (I suppose you have already known the fundamental of network protocols, such as TCP/UDP, IP, port number etc.)

1) What is socket?
Shortly put, a socket is a combination of an IP address and a port number of a two-way communication link between two programs on the network.

2) Types of sockets:
For old version of socket, it provides two types of sockets:
"Stream Socket": provide connection-oriented, reliable data transmission service, use TCP protocol
"Datagram Socket": provide connetionless service, packets may not be received in order, use UDP protocol

Now some new types of sockets have been added, such as: SOCK_RDM (Reliable message datagram), SOCK_SEQPACKET(pseudo-stream packet based on datagrams)

3) Flow of the stream socket communication

On the server side, there are seven steps:
a) create a socket (socket)
b) bind the socket with a local IP address and a port (bind)
c) set socket to 'listen' mode (listen)
d) wait for client's request, when it arrives, accept the request and return
a new socket for the connection (accept)
e) communicate with client using the new returned socket (send/recv)
f) return, wait for another client's request
g) close the socket (close)

On the client side, there are four steps:
a) create a socket (socket)
b) send request to the server ((connect))
c) communicate with server (send/recv)
d) close the socket (close)

4) Step-by-step programming

Follow the steps in 3), we first write the code for a TCP server. We use visual c++. You can find the source codes in the next post. Here is the explanation of the code. It's better to read the code and the following descriptions simultaneously.

<1> create a Win32 Console Application, set project name to TCPServer

<2> write a main function

<3> initialize the win32 socket DLL, call function WSAStartup();

int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);
WSAStartup has two jobs to do:
i) initiates use of Ws2_32.dll: if WinSock.dll or lower network subsystem is not initialized correctly or is not found, will return WSASYSNOTREADY.
ii) negotiate the socket version:
if requested version < MAX version supported by the WinSock.dll, use min(requested version, MAX version)
if requested version < MIN version supported by the WinSock.dll, return WSAVERNOTSUPPORTED, then call function WSACleanUP() to release resource that has been allocated for WSAStartup

The WSADATA or *LPWSADATA used in WSAStartup is a structure, it's defined as
typedef struct WSAData
{
WORD wVersion; // winsock version
WORD wHighVersion; // highest version in the current winsock library
char szDescription[WSADESCRIPTION_LEN+1];// for special use
char szSystemStatus[WSASYS_STATUS_LEN+1];// for special use
unsigned short iMaxSockets; // maximum num of sockets that can be
// opened, don't use it
unsighed short iMaxUdpDg; // maximum length of packet, don't use it
char FAR * lpVendorInfo; // reserved for vendor, never been used
} WSADATA, *LPWSADATA;


<4> create a socket

SOCKET WSAAPI socket(
int af, // for tcp-ip, it must be AF_INET or PF_INET
int type, // for winsock 1.1, only SOCKET_STREAM or SOCKET_DGRAM
int protocol // protocol to be used, is specific to the address family and
// socket type, set to 0 for default
);

If no error occurs, it will return a descriptor referencing the new socket. Otherwise, return INVALID_SOCKET.

<5> bind

int bind(
SOCKET s, // descriptor identifying an unbound socket
const struct sockaddr* name, // the address to assign to the socket,
// it may be varied with different
// address families, so we should use the next
// parameter to indicate the
// length of this address structure
int namelen // length of the value, in bytes
);


The sockaddr is defined as:
struct sockaddr {
u_short sa_family;
char sa_data[14];
};

For different address family, we should use differnt structure to replace this sockaddr.
For TCP/IP, sockaddr_in is used.
The definition of sockaddr_in is:

struct sockaddr_in {
short sin_family; // for IP address, it's AF_INET
u_short sin_port; // port number
struct in_addr sin_addr; // IP address of the host,
// should use "network byte order"
char sin_zero[8]; // padding bytes, to make the length equals to the
// sockaddr structure
};
where the "network byte order" is the same as the well-known big_endian order.

It will return 0, if successful; otherwise, return SOCKET_ERROR

<6> listen

int listen(
SOCKET s, // descriptor identifying a socket
int backlog // max length of the queue of pending connections
);

<7> accept

SOCKET accept(
SOCKET s, // identifying a socket
struct sockaddr* addr, // used to store the client's ip addr and port num
int* addrlen // length of the
);

<8> send/recv
int send(
SOCKET s,
const char* buf,
int len,
int flags
);

<9> Include the header file , add ws2_32.lib to the project, the operation in Visual C++ is:
project -> setting -> link -> Object/library modules, append ws2_32.lib to the end


<10> Then let's go to the client side. First add a new project to the current workspace, name it as "TCPClient".

<11> Similar as the Server code, first call WSAStartup to initialize the ws2_32.dll and negotiate the socket version, then create a socket

<12> connect

int connect(
SOCKET s,
const struct sockaddr* name,
int namelen
);

<13> After connect, call function send/recv, and finnaly call closesocket and WSACleanup as in server side.

<14> Include the header file and add ws2_32.lib to this project too

<15> Compile all files, run TCPServer first, then run TCPClient, we will get the following results, as shown in the figure

5) Some functions are used in the program for the conversion of the address, they are
htons(): converts a u_short from host to TCP/IP network byte order
htonl(): converts a u_long from host to TCP/IP network byte order
ntohs(): converts a u_short from TCP/IP network byte order to host byte order
ntohl(): converts a u_long from TCP/IP network byte order to host byte order
inet_ntoa(): converts an (Ipv4) Internet network address into a string in Internet standard dotted-decimal format
inet_addr(): converts a string containing an (Ipv4) Internet Protocol dotted address into a proper address for the IN_ADDR structure


No comments: