DWise1's Sockets Programming Pages

Working with Sockets


HARD HAT AREA

WATCH YOUR STEP

Introduction

Network programming is the writing of programs that will communicate over a network with programs running another computer. Sockets programming offers a standardized way to write these programs.

The Sockets programming model, AKA "Berkeley Sockets", consists of a set of data structures, predefined constants, and functions that perform system calls. In effect, sockets programming provides the low-level I/O that enables applications to each other across networks and to send and receive blocks of data between each other. Most of TCP/IP's plethora of protocols operate on the application level, whereas sockets programming only deals with two protocols: tcp and udp. The complex, innovative, and interesting part of a network program lies in the application's protocol, whereas the sockets part of the application is fairly standard and straight-forward and normally hardly changes at all from one application to the next. Indeed, in his book, Effective TCP/IP Programming: 44 Tips to Improve Your Network Programs, Jon Snader recommends that you create a library of skeleton sockets code that you can then reuse in all of your network programs (Tip #4).

Although it originated as a TCP/IP programming model, socket programming is flexible enough to be used for other protocol families, including UNIX domain, IPX/SPX, XEROX NS, X.25, SNA, DECnet, AppleTalk, and NetBEUI. And although it was originally written in C on UNIX, it has been ported to several other programming languages and operating systems; e.g., Perl, Visual BASIC, Delphi, Java, Python, Windows, and LabView. From what I've seen, the concepts and most of the function names are the same, so much of what you learn in one development environment should be transferable to the others.

My approach on this page is in C and mainly under Linux. On my Winsock Page I discuss how to apply the information on this page to Windows.

Please note that in the function descriptions given on this page, only the header files for UNIX/Linux are given. If you are developing under Windows and hence are using Winsock, then in each case replace those header files with either "winsock.h" or "winsock2.h".


Basic Overview

Most sockets applications involve a small set of sockets operations that tend to follow the same basic sequence. A common example would be a client/server application, which actually consists of two different applications: A most basic overview of the process would be:
  1. The server waits for a client to connect to it.
  2. A client connects to the server.
  3. The client and server exchange messages as "packets" which contain data and/or commands/requests.
  4. When the message exchange is completed, the client closes the connection.
  5. The server continues to wait for a client to connect to it.

Although there are a number of detailed ways to implement a server and a client, the basic steps remain essentially the same (differences between client's steps and server's steps are noted):

  1. [both] Create a socket with socket().
    This creates a socket file descriptor (in UNIX/Linux) or handle (Windows) that is required by most of the sockets functions and that the operating system needs to perform network operations.

  2. [both] Set up the socket address by filling in the sockaddr_in data structure with the IP address and port number.

  3. [server] Bind the socket to a port with bind().
    This uses the sockaddr_in data structure to bind the server's socket to a pre-determined known port so that the client will know which port to connect to. This step could be skipped in which case the operating system would automatically pick a port to bind the socket to, but there's no way to predict which port that will be.

  4. [server] Listen for a connection with listen().
    This changes the server's socket into a special type, a "listening socket", that waits for clients to attempt to make a connection. It can serve no other purpose after this point. This function will be called once and only once for every listening socket in the server.
    This is only used in the tcp protocol.

  5. [client] Connect to a Server with connect().
    This uses the sockaddr_in data structure to attempt to connect to the server whose address is in sockaddr_in.
    This is only used in the tcp protocol.

  6. [server] Accept a Connection with accept().
    This accepts the connection that a client is attempting and returns a sockaddr_in data structure containing the client's address and a new socket that is connected to that client and with which all communication with the client is to be performed.
    This is only used in the tcp protocol.

  7. [both] Send a message (tcp) with send().
    Send a block of data on the socket.

  8. [both] Receive a message (tcp) with recv().
    Read a block of data from the socket.

  9. [both] Send a message (udp) with sendto().
    Send a block of data to the address indicated in the sockaddr_in data structure.

  10. [both] Receive a message (udp) with recvfrom().
    Read a block of data from the socket. The source address of the message is returned in the sockaddr_in data structure.

  11. [both] Close the connection with shutdown().
    This terminates a tcp connection.
    This is only used in the tcp protocol.

  12. [both] Close the socket with close().
    This closes and destroys the socket.

Create a Socket

Before we can use a socket, we need to create it. When we create a socket, the system gives us a "socket handle", called a "descriptor" in UNIX/Linux, which is an integer value that the operating system uses to access the actual socket. In every function we perform on that socket, we need to provide that socket handle in order to identify which socket to use. In WinSock this handle is of a "special" type called SOCKET, which incidentally is an unsigned integer, and which can take on values that UNIX/Linux socket descriptors cannot.

So, in order to create a socket, you need to specify the protocol family (PF_INET in our case), the protocol (tcp or udp with the pre-defined constants IPPROTO_TCP and IPPROTO_UDP, respectively), and the "socket type" (SOCK_STREAM for tcp and SOCK_DGRAM for udp).

You create a socket with the function call:

socket()
Creates a socket

#include "sys/types.h"
#include "sys/socket.h"

int socket(int protocol_family, int type, int protocol);

  • protocol_family -- for our purposes, always PF_INET
  • type -- Type of socket (SOCK_STREAM or SOCK_DGRAM)
  • protocol -- Socket protocol (IPPROTO_TCP or IPPROTO_UDP)
On success, socket() returns the descriptor of the new socket.
On failure, it returns -1 and the error is in errno.

NOTE: the type and protocol fields must agree. In your code, this function call will always be:

sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
or
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

Set up the Socket Address

An address in sockets programming is defined by a unique IP-address/port/protocol combination and a connection (AKA "a socket") is uniquely identified by a pair of addresses.

The protocol (tcp or udp) was already established when you created the socket with the call to socket(). This section will show you how to define the IP address and port for the socket.

The sockets API provides data structures and functions for translating address data into the required format and for setting the address. These include functions for performing Domain Name Service (DNS) lookup and translation. However, I will describe all that on another page. After all, I don't want to scare you off yet.


The Generic Address Structure, struct sockaddr

The basic data structure for network addressing is struct sockaddr. Every sockets function that requires or returns an address structure uses this struct. It is declared thus:
#include "sys/socket.h"

struct sockaddr { unsigned short sa_family; /* Address family (e.g., AF_INET) */ char sa_data[14]; /* Protocol-specific address information */ };
I've stated elsewhere that sockets addressing is very flexible and struct sockaddr is why that is. There are over 20 different address families, each of which will redefine those fourteen bytes of sa_data differently. But since each of those redefinitions will fit inside a sockaddr variable, all you need to do is to declare a variable of your address family's redefined address structure, fill it, and then cast a pointer to it to a pointer to sockaddr when you pass it to a sockets function. The function itself then reads the address-family field, sa_family, in order to see how to interpret the rest of the address structure. It's really a lot more simple that it just sounded, as you will see in the examples.

If you wish to see what the different address families are, you can find a list of the pre-defined address family constants in the include file, linux/socket.h under Linux or in winsock.h under Windows (see table to the right). By the way, the protocol family constants used in the socket() function are actually the same as the corresponding address family constants and it is becoming increasingly common to use AF_INET in all cases instead of PF_INET.


The Internet Address Structure, struct sockaddr_in

For our purposes, we will be using the AF_INET form of the sockaddr structure, which is sockaddr_in and is declared thus:
#include "netinet/in.h"

struct in_addr
{
    unsigned long s_addr;         /* IP address (32 bits) */
};

struct sockaddr_in 
{
    unsigned short  sin_family;   /* Address family (e.g., AF_INET) */
    unsigned short  sin_port;     /* Address port (16 bits) */
    struct in_addr  sin_addr;     /* IP address (32 bits) */   
    char            sin_zero[8];  /* Not used */
};
NOTE: All values must be in network byte order
As you can see, the two fields of address data (two bytes each) plus the 8 bytes of padding make up the full 14 bytes in the sa_data field of sockaddr.

By the way, the reason that in_addr is struct is because it's actually a union (a feature in the C language that enables you to view the same location in memory as different data types, in this case as one unsigned long, as two unsigned shorts, or as four individual bytes. However, the other definitions are only of historical interest and are no longer put to any practical use. In all the sockets code I've seen, we only use the unsigned long s_addr declaration and so in many references that's all that's ever listed, even though you will see the union in the header file.


Actually Setting the Address

Given an IP address and a port number, here is how you stuff them into a sockaddr_in structure. This is most commonly done in in-line code, but I'm presenting it as a function to also give the data declarations:
void StuffSockAddr_in(struct sockaddr_in *addr, const char *IP, short int port)
{
    memset(addr, 0, sizeof(*addr));           /* Zero out structure */
    addr->sin_family      = AF_INET;         /* Internet address family */
    addr->sin_addr.s_addr = inet_addr(IP);   /* IP address */
    addr->sin_port        = htons(port);     /* Port */
}    
Notes: Curiously, the sin_family field does not need to be converted to network byte order. I just realized that and haven't figured it out yet.

inet_addr()
Converts a dotted-decimal IP address from a string to an unsigned long

#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"

unsigned long inet_addr(const char *addr);

  • addr -- Pointer to character string containing dotted-decimal IP address
On success, inet_addr() returns the binary form of the address in network byte order.
On failure, it returns -1 and the error is in errno.

htons(), htonl(), ntohs(), ntohl(),
Convert byte order between host and netword for short int or long int

#include "netinet/in.h"

short int htons(short int shortHost);
long int htonl(long int longHost);
short int ntohs(short int shortNet);
long int ntohl(long int longNet);

  • shortHost -- short int in host byte order
  • longHost -- long int in host byte order
  • shortNet -- short int in network byte order
  • longNet -- long int in network byte order
Returns the converted value.
There is no failure condition.


Caveat Programmator -- Replace inet_addr with inet_aton

I normally use inet_addr because that's what I had learned. However, that function is considered to be obsolete and it is advised against using it in new programs.

Here's the reason: to indicate failure, inet_addr returns INADDR_NONE, which is defined as 0xffffffff. However, that is also the valid return value for "255.255.255.255", which is commonly used as the universal broadcast address. Therefore, if inet_addr returns INADDR_NONE, you don't know whether it had failed or it had successfully converted "255.255.255.255".

It is advised to use inet_aton ("ASCII to number") instead of inet_addr. inet_aton removes any ambiguity by separating the error indication, which it returns as the function value, from the binary IP address, which it returns through a pointer parameter.

inet_aton()
Converts a dotted-decimal IP address from a string to a struct in_addr

#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"

int inet_aton(const char * cp, struct in_addr * inp);

  • cp -- Pointer to character string containing dotted-decimal IP address
  • inp -- Pointer to the in_addr struct that will receive the binary IP address in network byte order
inet_aton returns nonzero if the address is valid, zero if not.

However, inet_aton is not a "pin-for-pin" replacement for inet_addr, because it does not return the IP address per se, but rather it stuffs the IP address into a in_addr struct that is provided by the calling function. Here is how we would rewrite the StuffSockAddr_in function to use inet_aton:

void StuffSockAddr_in(struct sockaddr_in *addr, const char *IP, short int port)
{
    memset(addr, 0, sizeof(*addr));           /* Zero out structure */
    addr->sin_family      = AF_INET;         /* Internet address family */
    inet_aton(IP, &(addr->sin_addr));   /* IP address */
    addr->sin_port        = htons(port);     /* Port */
}    

Another nice thing about inet_aton is that it offers nice symmetry with the reverse function, inet_ntoa (number to ASCII), that converts a 32-bit binary IP address to a dotted-decimal string and which takes an in_addr struct:

inet_ntoa()
Converts a struct in_addr into an equivalent dotted-decimal IP address string

#include "sys/socket.h"
#include "netinet/in.h"
#include "arpa/inet.h"

char *inet_ntoa(struct in_addr in );

  • in -- the in_addr struct containing the binary IP address
inet_ntoa returns the dotted-decimal string. The string is returned in a statically allocated buffer, which subsequent calls will overwrite, so copy it if you want to save it for future use.


Miscellaneous Addressing Topics


Bind the Socket to a Port [server]

Now that we have created a socket, we need to associate it with a port. This is called "binding".

Actually, if we just start to use a socket without having called bind() first, the system automatically binds it to a randomly chosen port. This works just fine for a client, but not for a server. In order for a remote client to connect to a server, that client must know ahead of time which port the server is bound to. This means that a server must bind explicitly to a specific port. However, there can also be a valid need for a client to bind to a specific port, such as in a peer-to-peer application or in a broadcast client.

bind()
Binds a socket to a specific port

#include "sys/types.h"
#include "sys/socket.h"

int bind(int socket, struct sockaddr *localAddress, unsigned int addresslength);

  • socket -- the socket to be bound to the port
  • localAddress -- the address structure containing the IP address of the network interface and the port number. The special address ANY_ADDR can be used to allow the connection to be made on any of the host's intefaces.
  • addresslength -- the size in bytes of the sockaddr structure
On success, bind() returns a zero.
On failure, it returns -1 and the error is in errno. Usually EADDRINUSE.


Listen for a Connection [server]

This function is used for stream/TCP sockets only and is used in conjunction with accept().

In a TCP connection, one application, the server, binds to a well-known port and waits for a client to try to connect to it. This is called "listening" and it is performed by calling the listen() function.

listen() changes the socket to a special type whose only function is to process new attempts to connect on that port. It must already be bound to a local port; ie, bind() must have already been called.

When a client's connection request comes in, if the queue has not been filled (queue length is set with the "backlog" parameter), then the new connection request is placed in the queue. If the queue is full, then the new connection request will be rejected, which will result in an error in the client application. Queued connections are removed from the queue and completed with the accept() function, which also creates a new socket for communicating with the client.

listen() does not block. It returns immediately from the function call.

listen()
Sets a socket to listen for incoming connections
tcp sockets only

#include "sys/types.h"
#include "sys/socket.h"

int listen(int socket, int backlog);

  • socket -- Socket (returned from socket())
  • backlog -- Maximum number of new connections (sockets) to allow to wait
On success, listen() returns 0 On failure, it returns -1 and the error is in errno.


Connect to a Server [client]

Requests a connection to a specified port at a specified peer address. While it's most commonly used with the tcp protocol, it can also be used with udp. In the tcp protocol, success means that a TCP connection has been established. Failure can occur for any of several possible reasons; eg, failure to find the server system, no server was listening on that port, the server rejected the connection (eg, because the queue was full). In case of failure, you will need to examine the error code to determine the reason. Calling connect() initiates the "Triple Handshake".

If the client socket had not already been bound to a port with bind(), then connect() will bind it to a randomly assigned port and fill in its local address information as well as the peer's information. After this point, you may read the socket's local address information with getsockname() and its peer address information with getpeername().

connect()
Requests a connection to a peer

#include "sys/types.h"
#include "sys/socket.h"

int connect(int socket, struct sockaddr *peerAddress, int addressLength);

  • socket -- Socket (returned from socket())
  • peerAddress -- Populated sockaddr structure describing the peer socket address to connect to
  • addressLength -- Number of bytes in the peerAddress structure
On success, connect() returns zero.
On failure, it returns -1 (SOCKET_ERROR in Winsock) and the error is in errno.

getsockname()
Get a local socket's address information

#include "sys/socket.h"

int getsockname(int socket, struct sockaddr *localAddress, int addressLength);

  • socket -- Socket (returned from socket())
  • localAddress -- sockaddr structure to receive local address
  • addressLength -- Number of bytes in the localAddress structure
On success, getsockname() returns zero.
On failure, it returns -1.

getpeername()
Get the remote peer's address information

#include "sys/socket.h"

int getpeername(int socket, struct sockaddr *peerAddress, int addressLength);

  • socket -- Socket (returned from socket())
  • peerAddress -- sockaddr structure to receive peer address
  • addressLength -- Number of bytes in the peerAddress structure
On success, getpeername() returns zero.
On failure, it returns -1.


Accept a Connection [server]

This function is used for stream/TCP sockets only and is used in conjunction with listen().

accept() completes the process of establishing a TCP connection between the server and the client. It removes the first waiting entry in the listen queue, creates a new socket for it, and fills in that new socket's address information with both the local and the remote address information, which are accessible through getsockname() and getpeername() respectively.

However, if there is no pending connection waiting in the queue, then accept() won't return until one does come in. This means that accept() blocks; it's the first function we've considered here that does block. This means that that thread of code cannot continue until accept() returns. If the program only consists of a single thread of code, then the entire program "freezes" until a connection is made. This may be acceptable in the most simple of servers that will only handle one client at a time, but it is unacceptable in most serious applications.

Blocking is a non-trivial problem and a very real issue in sockets programming, that has led to a number of solutions, some of which are system-dependent. Some solutions include using multiple processes or threads (one for each blocking socket), changing the sockets to non-blocking mode which requires special handling, and using the select() function (to test whether there's anything for the blocking function call to process before actually calling it). I describe and discuss these in my "Dealing With and Getting Around Blocking Sockets" topic page.

And, obviously, if listen() had not been called to create a listening socket, then there would be no queue for waiting connections. In that case, accept() would return with an error.

accept()
Completes a connection with a client
tcp sockets only
Blocks

#include "sys/types.h"
#include "sys/socket.h"

int accept(int socket, struct sockaddr *clientAddress, int *addressLength);

  • socket -- Listening socket
  • clientAddress -- Receives client socket IP address and port
  • addressLength -- Length of sockaddr buffer for client address
On success, accept() returns the new socket descriptor.
On failure, it returns -1 (SOCKET_ERROR in Winsock) and the error is in errno.

Note:

There is a basic rule in sockets programming that you cannot use the same port for two different things at the same time. That only makes sense. If one process is listening on a port for incoming messages, another process cannot use the same port to send messages nor to also start listening.

That rule seems very straight-forward, but I got confused for a long time because of a misunderstanding about the new socket that accept() returns. I had associated ports and sockets too closely together, so I interpreted that new socket as also requiring a new port. Needless to say, I was very surprised when our Linux-server instructor ran the netstat utility on a web server and I saw every single client connecting to it through TCP port 80.

Remember from the discussion, "Ports and Sockets", that:

"Each socket is uniquely identified by a pair of addresses: the local address and the peer's address. This is important to remember, because we must be careful not to associate a socket too closely with a local port. There is a cardinal rule that a given socket can only be used for one connection at a time. But if we run a server, say a web server on TCP port 80, and ten clients connect to us, then we will see that our local TCP port 80 is involved in each of those ten connections. That appears to violate that cardinal rule. However, what is really happening is that each of those are different sockets, because each of them has a different peer address. We are not using the same socket ten times over, but rather we are using ten different sockets. There is no actual conflict nor violation of the rule."
So bear in mind that the new socket returned by accept() will be on the same local port as the listening socket; you can verify that with a call to getsockname(). But it is still a different socket.


Send a Message (tcp)

Sends the bytes contained in the buffer over the given connected socket. The actual format or contents of the data buffer are immaterial to the function.

When sent over a TCP socket, the size of the data can exceed the maximum size for a data packet. The tcp protocol will simply break that data up into multiple packets that get sent separately. Then the peer receiving the packets will put them back together again to reconstruct the original block of data. It should be noted that every send() will have a single corresponding recv() at the receiving peer. Even though the send() resulted in multiple packets, it appears to the receiving peer application as if that data had been sent in a single packet.

send()
Sends a message on a connected socket
connected sockets only; normally TCP, but could be UDP

#include "sys/types.h"
#include "sys/socket.h"

int send(int socket, const void *msg, unsigned int msglength, int flags);

  • socket -- Socket (must be in connected state)
  • msg -- Pointer to data to be transmitted
  • msglength -- Number of bytes to be sent
  • flags -- Control flags for special protocol features; set to O in most cases
On success, send() returns the number of bytes actually sent.
On failure, it returns -1 and the error is in errno.


Receive a Message (tcp)

The opposite number of send(), recv() takes the data received over the connected socket and copies to the buffer address provided to it for up to the maximum number specified.

Its return value is the number of bytes received, or -1 if there was an error. However, if the connection closes, then the return value will be zero.

recv() blocks until there is at least one byte of data to be read or else the connection closes. Therefore, this is another part of the program that will need to address the blocking problem.

recv()
Receives a message on a connected socket
connected sockets only; normally TCP, but could be UDP
Blocks

#include "sys/types.h"
#include "sys/socket.h"

int recv(int socket, void *rcvbuffer, int bufferlength, int flags);

  • socket -- Socket (must be in connected state)
  • rcvbuffer -- Where to put the data
  • bufferlength -- Maximum number of bytes to put in buffer
  • flags -- Control flags, 0 in most cases
On success, recv() returns the number of bytes received.
On failure, it returns -1 and the error is in errno.

One thing to keep in mind is that the packet that the other host had sent could have been fragmented into several smaller packets for transmission, which were then reassembled as they were received. That means that that when you start reading in data via recv(), you might have not yet received the entire packet. You may need to call recv() multiple times to read everything in. How do you know whether you've received the entire packet? That would part of the application protocol. Just something to keep in mind.


Sending a Message (udp)

sendto() sends the bytes contained in the buffer over the given socket. Very similar to send(), except that the destination address must be specified.

Normally used with a UDP datagram socket for sending a single packet. As such, the size of the block of data being sent can be no larger than what can be contained in a single data packet.

sendto()
Sends a message
normally used with UDP datagrams, but could also be used with a TCP connected socket

#include "sys/types.h"
#include "sys/socket.h"

int sendto(int socket, const void *msg, int msglength, int flags, struct sockaddr *destaddr, int destAddrLen);

  • socket -- Socket
  • msg -- Pointer to data to be transmitted
  • msglength -- Number of bytes to be sent
  • flags -- Control flags (O in most cases)
  • destaddr -- Destination address for data
  • destAddrLen -- Length of destination address structure
On success, sendto() returns the number of bytes sent.
On failure, it returns -1 and the error is in errno.


Receiving a Message (udp)

The opposite number of sendto(), recvfrom() takes the data received in a datagram and copies it to the buffer address provided to it for up to the maximum number specified. It also loads the sender's address information into the address structure passed to it.

It is very similar to recv(), except that the sender's address is also returned. And like recv(), recvfrom() also blocks until there is at least one byte of data to be read. Therefore, this is another part of the program that will need to address the blocking problem.

And it also differs from recv() in that you are assured of having received the either packet, by the very nature of UDP.

recvfrom()
Receives a datagram
normally used with UDP datagrams, but could also be used with a TCP connected socket
Blocks

#include "sys/types.h"
#include "sys/socket.h"

int recvfrom(int socket, void *buffer, int buffersize, int flags, struct sockaddr *fromAddr, unsigned int *fromAddrLen);

  • socket -- Socket (must be in connected state)
  • buffer -- Where to put the data
  • buffersize -- Max number of bytes to put in buffer
  • flags -- Control flags (O in most cases)
  • fromAddr -- Address data of sender
  • fromAddrLen -- Length of sender address structure
On success, recvfrom() returns the number of bytes received.
On failure, it returns -1 and the error is in errno.


Closing a Connection

shutdown() is used to close the connection in an orderly and graceful manner (yet another hot topic). This would normally only be done with TCP sockets, but it could also be done with UDP sockets that were connected.

Basically, with the parameters you indicate whether you are done sending or done receiving or done with both. With that, you can indicate your intention to terminate the session and close the connection. And with the proper choice of parameter, you can ensure that no data will be lost in the process.

Normally, this is the shutdown procedure for a TCP application:

  1. You will start by signalling that you will not send any more packets by calling shutdown(1).
  2. The remote peer detects your shutdown when recv() returns a zero.
  3. The peer finishes sending the rest of its data, if any.
  4. The peer also calls shutdown(1) to signal that it will send no more data.
  5. Since it already knows it won't receive anything more from you, the peer calls close() to close its socket.
  6. You detect the peer's shutdown when recv() returns a zero, whereupon you know you can safely close your socket.
And that essentially is how a "graceful shutdown" should work.

shutdown()
Closes a connection

#include "sys/socket.h"

int shutdown(int socket, int how);

  • socket -- Socket (must be in connected state)
  • how --
    1. Disallow further receiving
    2. Disallow further sending
    3. Disallow further receiving or sending
On success, shutdown() returns zero.
On failure, it returns -1 and the error is in errno.
WinSock's handling of the shutdown() function and of the shutdown process in general is considered as not being as graceful as in UNIX and there are warnings that it can cause unexpected errors. Be mindful of this when things aren't working exactly as you expect them to.

For example, in UNIX recv() returns a zero when the connection has been broken. Winsock is intended to do the same thing and sometimes does, but it could also return a -1 with an error of WSAECONNRESET, WSAECONNABORTED or WSAESHUTDOWN. Similarly, in UNIX send() will return a -1 and a EPIPE error, whereas in Winsock it could behave exactly as recv() by returning either a zero or a -1 with the same errors as recv().

So if you're working with Winsock, you will need to become familiar with all of its peculiarities.


Close the Socket

Just like closing a file (which is what you are doing on UNIX/Linux), close() closes the socket, releases that resource, and makes the socket cease to exist. All operations still pending on the socket, including any communications, are immediately terminated. Any subsequent attempts to use that socket (except to create a new one) will result in error. That socket is history. That's all she wrote.

close()
Closes the socket

#include "unistd.h"

int close(int socket);

  • socket -- The socket to be closed
On success, close() returns 0.
On failure, it returns -1 and the error is in errno.


The Winsock Way

Yet again, Winsock is a bit different. While almost everything on UNIX is a file, including sockets, Windows has not followed that philosophy. So while any kind of file in UNIX can be closed with the same close() function, in Windows system objects such as a SOCKET must be closed with a special function that handles that specific kind of system object.

Therefore, in Winsock you must use a special function, closesocket(). It works pretty much the same as the close() function on UNIX, but the effects of calling closesocket() can become quite involved depending on what the socket's options were set to, so you should read the help page on Microsoft's MSDN site for more detailed information (that page also links to the Winsock error codes).

closesocket()
Closes the Winsock socket

#include "winsock.h"
/* or "winsock2.h" */
int closesocket(SOCKET socket);

  • socket -- The Winsock socket to be closed
On success, close() returns 0.
On failure, it returns SOCKET_ERROR; retrieve the error code by calling WSAGetLastError().

Return to Top of Page
Return to DWise1's Sockets Programming Page
Return to DWise1's Programming Page

Contact me.


Share and enjoy!

First uploaded on 2004 April 15.
Updated on 2011 July 18.