Вы находитесь на странице: 1из 10

Sockets

What's a socket?
A server application normally listens to a specific port waiting for connection requests from a client. When a connection request arrives, the client and the server establish a dedicated connection over which they can communicate. During the connection process, the client is assigned a local port number, and binds a socket to it. The client talks to the server by writing to the socket and gets information from the server by reading from it. Similarly, the server gets a new local port number (it needs a new port number so that it can continue to listen for connection requests on the original port). The server also binds a socket to its local port and communicates with the client by reading from and writing to it. The client and the server must agree on a protocol--that is, they must agree on the language of the information transferred back and forth through the socket.

Definition: A socket is one end-point of a two-way communication link between two programs running on the network.

Client and Server Communication


There are two distinct types of socket network applications: Server and client. In simple words, servers provide services for clients and other servers whereas clients request services from servers. Servers and clients have different behaviors; therefore, the process of creating them is different. What follows is the general model for creating a streaming TCP/IP server and client. The steps in creating (and difference between) server and client sockets are listed below. Server side programs: 1. 2. 3. 4. 5. 6. 7. Initialize WSA WSAStartup(). Create a socket socket(). Bind the socket bind(). Listen on the socket listen(). Accept a connection accept(), connect(). Send and receive data recv(), send(), recvfrom(), sendto(). Disconnect closesocket().

1. WSAStartup
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
int wsaret=WSAStartup(0x101,&wsaData);

Before calling any winsock function, we need to initialize the winsock library. This is done with WSAStartup. It takes two parameters: wVersionRequested Highest version of Windows Sockets support that the caller can use. The highorder byte specifies the minor version (revision) number; the low-order byte specifies the major version number. lpWSAData Pointer to the WSADATA data structure that is to receive details of the Windows Sockets implementation. The function returns zero if it succeeded, otherwise you can call WSAGetLastError to see what went wrong. WSAGetLastError is the winsock equivalent of the win32 APIs GetLastError, it retrieves the code of the last occurred error.

2. socket
SOCKET socket(int af, int type, int protocol);
m_SListenClient=socket(AF_INET,SOCK_STREAM,0);

The socket function creates a new socket and returns a handle to it. The handle is of type SOCKET and is used by all functions that operate on the socket. The only invalid socket handle value is INVALID_SOCKET (defined as ~0), all other values are legal (this includes the value zero!). Its parameters are: af The address family to use. Use AF_INET to use the address family of TCP & UDP. type The type of socket to create. Use SOCK_STREAM to create a streaming socket (using TCP), or SOCK_DGRAM to create a diagram socket (using UDP). For more information on socket types, see the previous chapter. protocol The protocol to be used, this value depends on the address family. You can specify IPPROTO_TCP here to create a TCP socket. The return value is a handle to the new socket, or INVALID_SOCKET if something went wrong.

3. bind
int bind(SOCKET s, const struct sockaddr *name, int namelen);

By binding a socket you assign an address to a socket. Bind's parameters are: s The unbound socket you want to bind. name Pointer to a sockaddr structure that contains the address to assign to the socket. namelen Size of the structure pointed to by name. For TCP/IP, the sockadrr_in structure can be used as usually. Let's look at this code first:
sockaddr_in local.sin_family = AF_INET; local.sin_port = htons((u_short)8084); local.sin_addr.S_addr = INADDR_ANY; // use default // Bind socket to port 8084 if (bind(m_SListenClient, (sockaddr*)(&sockAddr), sizeof(sockAddr))!=0) { // error handling code } local;

As you can see, a sockaddr_in structure is filled with the necessary information. The address family is AF_INET for TCP/IP. In the example, we bind the socket to port number 8084, but not to an IP number. By specifying the INADDR_ANY value as IP address, winsock will choose an address for you. This can be very useful for PCs with multiple network adapters (and thus multiple IPs). If you do want to bind to a specific IP, just convert the IP to a DWORD in network byte order and put it in the structure. Something similar is possible with the port number; when you specify 0 as the port number winsock will assign a unique port. However, most of the time you want to bind to a specific port number. Binding is usually done before putting the socket in a listening state, to make the socket listen on the right port number (and optionally an IP number). Although you can also bind a socket before connecting it, this is not commonly done because the address of the socket on the client side is not important most of the time.

4. listen
int listen(SOCKET s, int backlog); The listen function puts a socket in the listening state, that is it will be listening for incoming connections. It has two parameters: s The bound, unconnected socket you want to set into the listening state. backlog Maximum length of the queue of pending connections. The backlog parameter can be set to specify the length of the queue of pending connections that have not yet been accepted. Usually, we can use the default value SOMAXCONN, allowing the underlying service provider to choose a reasonable value. Before listen is called, the socket must have been bound to an address, For example, if you bind a socket to port 80 and then call listen on the socket, all incoming connections on port 80 will be routed to your application. To actually accept the connection, another function called accept is available. The following code shows how to call the listen function on a socket that has been bound already:
/* This code assumes the socket specified by hSocket is bound with the bind function */ if (listen(m_SListenClient, 10)!=0) { // error handling code }

5. accept
SOCKET accept(SOCKET s, struct sockaddr *addr, int *addrlen); When the socket is in the listening state and an incoming connection arrives, you can accept it with the accept function. s The socket that has been placed in a listening state with the listen function.

addr Optional pointer to a buffer that receives the address of the remote socket. This parameter is a pointer to a sockaddr structure, but its exact structure is determined by the address family. addrlen Optional pointer to an integer that contains the length of addr. Before calling the function, the value should be the size of the buffer pointed to by addr. On return, the value is the size of the data returned in the buffer. when a connection is accepted a new socket is created on the server side. This new socket is connected to the client socket, all operations on that connection are done with that socket. The original listening socket is not connected, but instead listens for more incoming connections.
sockaddr_in from; int fromlen=sizeof(from); m_SClient=accept(m_SListenClient, (struct sockaddr*)&from,&fromlen); if(m_SClient != INVALID_SOCKET)

{ // error handling code }

If accept succeeds, a connection is established and the return value is a new socket handle that is the server side of the new connection. Optionally, you can set the addr and addrlen parameters that will receive a sockaddr structure containing the remote address information (IP & port number).

6. send and recv


int send(SOCKET s, const char *buf, int len, int flags); s The connected socket to send data on. buf Pointer to a buffer containing the data to send len Length of the data pointed to by buf.

flags Specifies the way in which the call is made. int recv(SOCKET s, char *buf, int len, int flags); s The connected socket to receive data from. buf Pointer to a buffer that will receive the data. len Length of the buffer pointed to by buf. flags Specifies the way in which the call is made. To transfer data on a connection, we use the send and recv functions. Send sends the data in the buffer on the socket and returns the number of bytes sent. Recv receives the data that is currently available at the socket and stores it in the buffer. The flags parameter can usually be set to zero for both recv and send. In blocking mode, send will block until all data has been sent (or an error occurred) and recv will return as much information as is currently available, up to the size of the buffer specified. This example of recv and send on a connected socket in blocking mode will just send back all data it receives.
char buffer[128]; while(true) { // Receive data int bytesReceived = recv(hRemoteSocket, buffer, sizeof(buffer), 0); if (bytesReceived==0) // connection closed { break; } else if (bytesReceived==SOCKET_ERROR) { // error handling code }

// Send received data back if (send(hRemoteSocket, buffer, bytesReceived, 0)==SOCKET_ERROR) { // error handling code } } 7. Close the connection

Now that our chat is over, we must close the connection. In our case the connection is closed by the server the moment it finishes sending the file, but that doesn't matter. We need to close our socket and release the resource. In more complex chats we usually call shutdown() before we call closesocket() to ensure that the buffers are flushed. Otherwise we might encounter some data loss.
closesocket(conn);

Program Flow of a simple TCP client


1. 2. 3. 4. 5. 6. 7.
1.

Initialize WinSock library using WSAStartup() Create a socket() Retrieve host information using gethostbyname()/gethostbyaddr() Connect to the server using the socket we created, using connect() Send and Receive data using send()/recv() till our tcp chat is over Close the socket connection using closesocket() De-Initialize WinSock using WSACleanup()

Initialize WinSock

As with every other WinSock program we need to initialize the WinSock library. Basically it is also a kind of check to see if WinSock is available on the system in the precise version we expect it to be.
int wsaret=WSAStartup(0x101,&wsaData); if(wsaret!=0) { return; } 2. Create the SOCKET

The socket is the entity that acts as the endpoint between the client and the server. When a client is connected to a server, there are two sockets. The socket at the client side and the corresponding socket at the server side. Lets call them CLIENTSOCK and SERVERSOCK. When the client uses send() on CLIENTSOCK the server can use recv() on the SERVERSOCK to receive what the client sends. Similarly the

reverse is also true. For our purposes we create the socket using a function called socket().
SOCKET conn; conn=socket(AF_INET,SOCK_STREAM,0); if(conn==INVALID_SOCKET) return; 3. Getting host information

Obviously we need to get info about the host [the server] before we can connect to it. There are two functions we can use - gethostbyname() and gethostbyaddr(). The gethostbyname() function is used when we have the DNS name of our server, something like codeproject.com or ftp.myserver.org. The gethostbyaddr() function is used when we actually have the IP address of the server to connect to, something like 192.168.1.1 or 202.54.1.100. Obviously we would want to give our end user the option of entering either a DNS name or an IP address. Thus for making that part of it transparent to him, we do a little trick as shown below. We use the function inet_addr() on the entered string. The inet_addr() function converts an IP address into a standard network address format. Thus if it returns failure, we now know that the string cannot be an IP address, if it succeeds we assume that it was a valid IP address.
addr=inet_addr(servername); hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET); if(hp==NULL) { closesocket(conn); return; } 4. Connecting to the server

The connect() function is used to establish a connection to the destination server. We pass it the socket we created earlier as well as a sockaddr structure. We populate the sockaddr with the host address returned by gethostbyname()/gethostbyaddr(), as well as enter a valid port to connect to.
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr); server.sin_family=AF_INET; server.sin_port=htons(m_iServerPort); if(connect(conn,(struct sockaddr*)&server,sizeof(server))) { closesocket(conn); return; }

5. Chatting

Once the socket connection is established the client and the server can send() and recv() data between themselves. This is popularly referred to as TCP chatting. In our particular case we need to HTTP chat, which is comparatively simple when you consider other slightly more complicated protocols like SMTP or POP3. The HTTP GET command is used to retrieve a file from the HTTP server. This might be an HTML file or an image file or a zip or an MP3 or whatever. It is send thus [in it's simplest form]. There are other slightly more complex ways of using this command.
char buffer[128]; while(true) { // Receive data int bytesReceived = recv(hRemoteSocket, buffer, sizeof(buffer), 0); if (bytesReceived==0) // connection closed { break; } else if (bytesReceived==SOCKET_ERROR) { // error handling code } // Send received data back if (send(hRemoteSocket, buffer, bytesReceived, 0)==SOCKET_ERROR) { // error handling code } }

6. Close the connection

Now that our chat is over, we must close the connection. In our case the connection is closed by the server the moment it finishes sending the file, but that doesn't matter. We need to close our socket and release the resource. In more complex chats we usually call shutdown() before we call closesocket() to ensure that the buffers are flushed. Otherwise we might encounter some data loss.
closesocket(conn);

7.De-Initialize WinSock

We call WSACleanup() to conclude our usage of WinSock.


WSACleanup();

Вам также может понравиться