Unix Socket Programming Interview Preparation Guide
Strengthen your Unix Socket Programming interview skills with our collection of 62 important questions. Each question is designed to test and expand your Unix Socket Programming expertise. Suitable for all experience levels, these questions will help you prepare thoroughly. Download the free PDF to have all 62 questions at your fingertips. This resource is designed to boost your confidence and ensure youre interview-ready.62 Unix Socket Programming Questions and Answers:
1 :: How do Sockets Work?
The implementation is left up to the vendor of your particular unix, but from the point of view of the programmer, connection-oriented sockets work a lot like files, or pipes. The most noticeable difference, once you have your file descriptor is that read() or write() calls may actually read or write fewer bytes than requested. If this happens, then you will have to make a second call for the rest of the data. There are examples of this in the source code that accompanies the faq.
2 :: What is with the second parameter in bind()?
The man page shows it as "struct sockaddr *my_addr". The sockaddr struct though is just a place holder for the structure it really wants. You have to pass different structures depending on what kind of socket you have. For an AF_INET socket, you need the sockaddr_in structure. It has three fields of interest:
sin_family
Set this to AF_INET.
sin_port
The network byte-ordered 16 bit port number
sin_addr
The host's ip number.
This is a struct in_addr,
which contains only one field,
s_addr which is a u_long.
sin_family
Set this to AF_INET.
sin_port
The network byte-ordered 16 bit port number
sin_addr
The host's ip number.
This is a struct in_addr,
which contains only one field,
s_addr which is a u_long.
3 :: If bind() fails, what should I do with the socket descriptor?
If you are exiting, I have been assured by Andrew that all unixes will close open file descriptors on exit. If you are not exiting though, you can just close it with a regular close() call.
4 :: When should I use shutdown()?
shutdown() is useful for deliniating when you are done providing a request to a server using TCP. A typical use is to send a request to a server followed by a shutdown(). The server will read your request followed by an EOF (read of 0 on most unix implementations). This tells the server that it has your full request. You then go read blocked on the socket. The server will process your request and send the necessary data back to you followed by a close. When you have finished reading all of the response to your request you will read an EOF thus signifying that you have the whole response. It should be noted the TTCP (TCP for Transactions -- see R. Steven's home page) provides for a better method of tcp transaction management.
5 :: Code Sample: a very simple client process.
/*
*The quick example below is a fragment of
* a very simple client process.
* After establishing the connection with
* the server it forks. Then
* child sends the keyboard input to the
* server until EOF is received and
* the parent receives answers from the server.
*
* variables declarations and error handling are omitted
*/
s=connect(...);
if( fork() ){
/*The child, it copies its stdin to the socket*/
while( gets(buffer) >0)
write(s,buf,strlen(buffer));
close(s);
exit(0);
}
else {
/* The parent, it receives answers */
while( (l=read(s,buffer,sizeof(buffer)){
do_something(l,buffer);
/* Connection break from the server is assumed */
/* ATTENTION: deadlock here */
wait(0); /* Wait for the child to exit */
exit(0);
}
What do we expect? The child detects an EOF from its stdin, it closes the socket (assuming connection break) and exits. The server in its turn detects EOF, closes connection and exits. The parent detects EOF, makes the wait() system call and exits. What do we see instead? The socket instance in the parent process is still opened for writing and reading, though the parent never writes. The server never detects EOF and waits for more data from the client forever. The parent never sees the connection is closed and hangs forever and the server hangs too. Unexpected deadlock!
You should change the client fragment
as follows:
if( fork() ) {
/* The child */
while( gets(buffer) }
write(s,buffer,strlen(buffer));
shutdown(s,1);
/* Break the connection
for writing, The server will detect EOF now.
Note: reading from the socket is still allowed.
The server may send some more data
after receiving EOF, why not? */
exit(0);
}
*The quick example below is a fragment of
* a very simple client process.
* After establishing the connection with
* the server it forks. Then
* child sends the keyboard input to the
* server until EOF is received and
* the parent receives answers from the server.
*
* variables declarations and error handling are omitted
*/
s=connect(...);
if( fork() ){
/*The child, it copies its stdin to the socket*/
while( gets(buffer) >0)
write(s,buf,strlen(buffer));
close(s);
exit(0);
}
else {
/* The parent, it receives answers */
while( (l=read(s,buffer,sizeof(buffer)){
do_something(l,buffer);
/* Connection break from the server is assumed */
/* ATTENTION: deadlock here */
wait(0); /* Wait for the child to exit */
exit(0);
}
What do we expect? The child detects an EOF from its stdin, it closes the socket (assuming connection break) and exits. The server in its turn detects EOF, closes connection and exits. The parent detects EOF, makes the wait() system call and exits. What do we see instead? The socket instance in the parent process is still opened for writing and reading, though the parent never writes. The server never detects EOF and waits for more data from the client forever. The parent never sees the connection is closed and hangs forever and the server hangs too. Unexpected deadlock!
You should change the client fragment
as follows:
if( fork() ) {
/* The child */
while( gets(buffer) }
write(s,buffer,strlen(buffer));
shutdown(s,1);
/* Break the connection
for writing, The server will detect EOF now.
Note: reading from the socket is still allowed.
The server may send some more data
after receiving EOF, why not? */
exit(0);
}
6 :: What are Sockets?
Sockets are just like "worm holes" in science fiction. When things go into one end, they (should) come out of the other. Different kinds of sockets have different properties. Sockets are either connection- oriented or connectionless. Connection-oriented sockets allow for data to flow back and forth as needed, while connectionless sockets (also known as datagram sockets) allow only one message at a time to be transmitted, without an open connection. There are also different socket families. The two most common are AF_INET for internet connections, and AF_UNIX for unix IPC (interprocess communication). As stated earlier, this FAQ deals only with AF_INET sockets.
7 :: How can I tell when a socket is closed on the other end?
If the peer calls close() or exits, without
having messed with SO_LINGER, then our
calls to read() should return 0. It is
less clear what happens to write() calls
in this case; I would expect EPIPE, not
on the next call, but the one after.
If the peer reboots, or sets l_onoff = 1,
l_linger = 0 and then closes, then we
should get ECONNRESET (eventually) from
read(), or EPIPE from write().
When write() returns EPIPE, it also
raises the SIGPIPE signal - you never
see the EPIPE error unless you
handle or ignore the signal.
If the peer remains unreachable,
we should get some other error.
Don't think that write() can legitimately
return 0. read() should return 0 on
receipt of a FIN from the peer, and on
all following calls.
So yes, you must expect read() to return 0.
As an example, suppose you are receiving
a file down a TCP link; you
might handle the return from read() like this:
rc = read(sock,buf,sizeof(buf));
if (rc > 0)
{
write(file,buf,rc);
/* error checking on file omitted */
}
else if (rc == 0)
{
close(file);
close(sock);
/* file received successfully */
}
else /* rc < 0 */
{
/* close file and delete it, since data
is not complete report error, or whatever */
}
having messed with SO_LINGER, then our
calls to read() should return 0. It is
less clear what happens to write() calls
in this case; I would expect EPIPE, not
on the next call, but the one after.
If the peer reboots, or sets l_onoff = 1,
l_linger = 0 and then closes, then we
should get ECONNRESET (eventually) from
read(), or EPIPE from write().
When write() returns EPIPE, it also
raises the SIGPIPE signal - you never
see the EPIPE error unless you
handle or ignore the signal.
If the peer remains unreachable,
we should get some other error.
Don't think that write() can legitimately
return 0. read() should return 0 on
receipt of a FIN from the peer, and on
all following calls.
So yes, you must expect read() to return 0.
As an example, suppose you are receiving
a file down a TCP link; you
might handle the return from read() like this:
rc = read(sock,buf,sizeof(buf));
if (rc > 0)
{
write(file,buf,rc);
/* error checking on file omitted */
}
else if (rc == 0)
{
close(file);
close(sock);
/* file received successfully */
}
else /* rc < 0 */
{
/* close file and delete it, since data
is not complete report error, or whatever */
}
8 :: How do I get the port number for a given service?
Use the getservbyname() routine. This will return a pointer to a servent structure. You are interested in the s_port field, which contains the port number, with correct byte ordering (so you don't need to call htons() on it). Here is a sample routine:
/* Take a service name, and a service type, and return a port number. If the service name is not found, it tries it as a decimal number. The number returned is byte ordered for the network. */
int atoport(char *service, char *proto)
{
int port;
long int lport;
struct servent *serv;
char *errpos;
/* First try to read it from /etc/services */
serv = getservbyname(service, proto);
if (serv != NULL)
port = serv->s_port;
else {
/* Not in services, maybe a number? */
lport = strtol(service,&errpos,0);
if ( (errpos[0] != 0) || (lport < 1)
|| (lport > 5000) )
return -1;
/* Invalid port address */
port = htons(lport);
}
return port;
}
/* Take a service name, and a service type, and return a port number. If the service name is not found, it tries it as a decimal number. The number returned is byte ordered for the network. */
int atoport(char *service, char *proto)
{
int port;
long int lport;
struct servent *serv;
char *errpos;
/* First try to read it from /etc/services */
serv = getservbyname(service, proto);
if (serv != NULL)
port = serv->s_port;
else {
/* Not in services, maybe a number? */
lport = strtol(service,&errpos,0);
if ( (errpos[0] != 0) || (lport < 1)
|| (lport > 5000) )
return -1;
/* Invalid port address */
port = htons(lport);
}
return port;
}
9 :: How do I properly close a socket?
This question is usually asked by people who try close(), because they have seen that that is what they are supposed to do, and then run netstat and see that their socket is still active. Yes, close() is the correct method. To read about the TIME_WAIT state, and why it is important, refer to ``2.7 Please explain the TIME_WAIT state.''.
10 :: What is the difference between close() and shutdown()?
Generally the difference between close() and shutdown() is: close() closes the socket id for the process but the connection is still opened if another process shares this socket id. The connection stays opened both for read and write, and sometimes this is very important. shutdown() breaks the connection for all processes sharing the socket id. Those who try to read will detect EOF, and those who try to write will reseive SIGPIPE, possibly delayed while the kernel socket buffer will be filled. Additionally, shutdown() has a second argument which denotes how to close the connection: 0 means to disable further reading, 1 to disable writing and 2 disables both.