This Document Describes Winsock Programming Considerations
This Document Describes Winsock Programming Considerations
What is a socket?
A socket is very much like a telephone - it's the endpoint of a two-way communications channel.
By connecting two sockets together you can pass data between processes, even processes
running on different computers, just as you can talk over the telephone once you've made a
phone call connecting your telephone with someone else's.
If the initialization function (or most of the other WinSock functions) fails you can get additional
error information by calling WSAGetLastError(), which returns an error code indicating the
cause of the failure.
Similarly you must call the WSACleanup() function before your program exits to properly shut
down the library. In Win32 applications this good form although not strictly necessary; it is
critical in Win16 or Win32s applications.
Address resolution
every socket has a unique address made up of a couple of different parts:
The first part is the IP address, a four-digit number usually written as four decimal numbers
separated by periods (e.g. 192.9.200.10), which specifies which computer you want to talk to.
Every computer on the Internet has at least one IP address.
The second is the port number, allowing more than one conversation to take place
per computer - much like extension numbers in an office. An application may either
pick a port number to use (some are, of course, reserved) or request a random port
when assigning an address to a socket.
After creating a socket, we must give the socket an address to listen to .The bind()
function is used to do this (it binds a socket to an address, hence the name). An
internet socket address is specified using the sockaddr_in structure, which contains
fields that specify the address family, address, and port number for a socket. A
pointer to this structure is passed to functions like bind() which need an address.
Because sockets are intended to support more than one address family it's
necessary to cast the sockaddr_in pointer to a sockaddr structure pointer to avoid
compiler warnings.
If you are busy handling a connection, the connection request will wait until you can
deal with it. The listen() function is used to set the maximum number of requests
(up to a maximum of five, usually) that will be queued before requests start being
denied.
After you create a socket to get calls, you must wait for calls to that socket. The
accept() function is used to do this. Calling accept() is analogous to picking up the
telephone if it's ringing. Accept() returns a new socket which is connected to the
caller.
A program which listens for incoming socket connections usually calls accept() in a
loop and handles each incoming connection.
creates the socket, sets it up, and calls a particular port number on a particular
computer, returning a connected socket through which data can flow.
Unlike when you're reading and writing a file, the network can only send or receive a limited
amount of data at a time. As a result you can ask for many characters but you'll often get back
fewer than you asked for. One way to handle this is to loop until you have received the number of
characters that you want. A simple function to read a given number of characters into a buffer is:
int read_data(SOCKET s, /* connected socket */
char *buf, /* pointer to the buffer */
int n /* number of characters (bytes) we want */
)
{ int bcount; /* counts bytes read */
int br; /* bytes read this pass */
bcount = 0;
br = 0;
while (bcount < n) { /* loop until full buffer */
if ((br = recv(s, buf, n - bcount)) > 0) {
bcount += br; /* increment byte counter */
buf += br; /* move buffer ptr for next read */
}
else if (br < 0) /* signal an error to the caller */
return -1;
}
return bcount;
}
void do_something(SOCKET);
main()
{ SOCKET s;
/* this is the function that plays with the socket. it will be called
* after getting a connection.
*/
void do_something(SOCKET s)
{
/* do your thing with the socket here
:
:
*/
}
hp = gethostbyname(hostname);
if (hp == NULL) /* we don't know who this host is */
return INVALID_SOCKET;
memset(&sa,0,sizeof(sa));
memcpy((char *)&sa.sin_addr, hp->h_addr, hp->h_length); /* set address */
sa.sin_family = hp->h_addrtype;
sa.sin_port = htons((u_short)portnum);
WSACleanup();
return 1;
}
Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Socket == INVALID_SOCKET)
{
printf("Unable to create the socket.\n");
WSACleanup();
return 1;
}
memset(&ServerInfo, 0, sizeof(ServerInfo));
memcpy(&ServerInfo.sin_addr, HostEnt>h_addr, HostEnt>h_length);
ServerInfo.sin_family = HostEnt>h_addrtype; // Always AF_INET
ServerInfo.sin_port = htons(80);
if (connect(Socket, (struct sockaddr*) &ServerInfo, sizeof(ServerInfo)) == 1)
{
printf("Unable to connect to the server.\n");
WSACleanup();
return 1;
}
printf("Successfully connected to the server!\n");
closesocket(Socket);
WSACleanup();
return 0;
Blocking Input/Output
The simplest form of I/O in Windows Sockets 2 is blocking I/O. Sockets are created in
blocking mode by default. Any I/O operation with a blocking socket will not return until
the operation has been fully completed. Thus, any thread can only execute one I/O
operation at a time. For example, if a thread issues a receive operation and no data is
currently available, the thread will block until data becomes available and is placed into
the thread's buffer. Although this is simple, it is not necessarily the most efficient way to
do I/O. Blocking is strongly discouraged due to the non-preemptive nature of the
operating system.