0% found this document useful (0 votes)
17 views12 pages

Understanding TCP Socket Functions and Servers

The document provides an overview of various concepts in network programming, focusing on TCP socket operations, including the usage of shutdown() and close() functions, and the implementation of a TCP Echo Server. It also discusses I/O multiplexing with pselect(), socket timeouts, and the differences between read()/write() and recv()/send() functions. Additionally, it covers Unix Domain Sockets, the implementation of iterative and concurrent servers, and techniques for passing file descriptors between processes.

Uploaded by

raghavendra raju
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views12 pages

Understanding TCP Socket Functions and Servers

The document provides an overview of various concepts in network programming, focusing on TCP socket operations, including the usage of shutdown() and close() functions, and the implementation of a TCP Echo Server. It also discusses I/O multiplexing with pselect(), socket timeouts, and the differences between read()/write() and recv()/send() functions. Additionally, it covers Unix Domain Sockets, the implementation of iterative and concurrent servers, and techniques for passing file descriptors between processes.

Uploaded by

raghavendra raju
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Network Programming

Q1. What is the purpose of the shutdown() function in TCP


sockets, and how is it different from close()?

In TCP socket programming, proper connection termination ensures clean


communication and resource management. Two key system calls used are
shutdown() and close().

The shutdown() function allows selective disabling of communication over


a socket. It is used when one end wants to stop sending or receiving data
while keeping the socket open for the other direction. The syntax is:

int shutdown(int sockfd, int how);

 how can be:

o SHUT_RD (0): Disables further reading.

o SHUT_WR (1): Disables further writing.

o SHUT_RDWR (2): Disables both.

The close() function, on the other hand, completely closes the socket
descriptor and releases all associated resources. Once closed, the socket
is no longer usable.

Differences:

Aspect shutdown() close()

Behavio
Partial or full shutdown Full socket closure
r

Resourc Does not free socket Frees file


e immediately descriptor

Graceful, controlled Complete


Usage
termination termination

Example: In a client-server model, the client may send data, then call
shutdown(sockfd, SHUT_WR) to indicate it has finished sending. The
server can continue responding. Later, close() can be used for full closure.

Q2. How do you write a basic TCP Echo Server using the socket
API, and what are its uses and limitations?
A TCP Echo Server is a simple server that sends back whatever data it
receives from the client. It is used to demonstrate TCP bidirectional
communication.

Steps to build a TCP Echo Server:

1. Create a socket using socket().

2. Bind to a port using bind().

3. Listen for connections using listen().

4. Accept incoming client using accept().

5. Read data from the client using recv().

6. Echo the same data back using send().

Sample Code:

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

bind(sockfd, ...);

listen(sockfd, 5);

int clientfd = accept(sockfd, ...);

recv(clientfd, buffer, ...);

send(clientfd, buffer, ...);

Applications:

 Testing network communication

 Educational tool for learning sockets

 Base model for more complex servers

Limitations:

 Handles only one client at a time (iterative)

 Lacks concurrency and error handling

 Not suitable for high-traffic systems

Q3. What is pselect() in I/O multiplexing, and how is it better than


select() for handling signals?

pselect() is a system call used in I/O multiplexing that monitors multiple


file descriptors and allows signal-safe operations, offering an improvement
over select().
Syntax:

int pselect(int nfds, fd_set *readfds, ..., const struct timespec *timeout,
const sigset_t *sigmask);

The sigmask parameter allows atomic signal masking during wait, making
pselect() more suitable for programs that also handle signals.

Comparison with select():

Feature select() pselect()

Signal-safe? No Yes

Timeout Microsecon
Nanoseconds
precision ds

Supported via
Signal masking Not possible
sigmask

Use-case Example: A server that needs to wait for both I/O and a signal
like SIGINT (Ctrl+C) can use pselect() to avoid race conditions.

Advantages:

 Safer with signal handling

 Ideal for real-time systems

 More reliable for event-driven models

Conclusion: pselect() enhances select() by ensuring safe signal handling


during I/O multiplexing. It is preferred in applications that need to respond
to both socket events and asynchronous signals.

Q4. What are socket timeouts, and how can you set read/write
timeouts using setsockopt()?

Socket timeouts define how long a socket operation (like reading or


writing) can block before timing out. This is essential to prevent indefinite
waits during communication failures.

Setting timeouts: Use the setsockopt() function with SO_RCVTIMEO (for


read) and SO_SNDTIMEO (for write) options.

Syntax:

struct timeval tv;

tv.tv_sec = 5; // 5 seconds
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

Benefits:

 Prevents socket hang due to network issues

 Enhances user experience in real-time systems

 Enables retry or fallback logic in applications

Effect: If the timeout expires, recv() or send() returns -1 and sets errno to
EAGAIN or EWOULDBLOCK.

Use-cases:

 HTTP clients waiting for server responses

 Network devices with unstable connections

 Time-sensitive communication (e.g., games, VoIP)

Q5. How do read()/write() differ from recv()/send() in socket


programming, and when should you use each?

In socket programming, two sets of functions are used for data


transmission: read()/write() (generic file operations) and recv()/send()
(socket-specific).

Key Differences:

Aspec
read()/write() recv()/send()
t

Works on all file


Scope Works only on sockets
descriptors

Supports flags like MSG_PEEK,


Flags No extra control
MSG_DONTWAIT

Simpler, general-
Usage Advanced socket handling
purpose

Syntax:

ssize_t read(int fd, void *buf, size_t count);

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

Use-case:

 Use recv() and send() when advanced options (flags) are required.
 Use read() and write() in simple TCP applications where flags are not
necessary.

Example:

char buffer[100];

recv(sockfd, buffer, sizeof(buffer), MSG_PEEK); // Reads without consuming

Q6. What are readv() and writev() system calls, and how do they
improve I/O efficiency?

readv() and writev() are system calls used for scatter/gather I/O,
allowing multiple buffers to be read from or written to a file/socket in a
single operation.

Syntax:

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);

ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

The iovec structure is defined as:

struct iovec {

void *iov_base; // Pointer to data

size_t iov_len; // Length of data

};

Advantages:

 Minimizes system calls (no repeated read()/write()).

 Increases performance by reducing user-kernel context switches.

 Ideal for applications that separate headers and body (e.g., web
servers).

Example:

char header[] = "Header\n", body[] = "Content\n";

struct iovec iov[2] = {

{header, strlen(header)},

{body, strlen(body)}

};

writev(sockfd, iov, 2);


Use-cases:

 Web servers sending multiple parts (headers + data)

 Logging services

 Protocols with metadata + payload separation

Q7. What is the socket address structure for Unix domain sockets,
and how is it different from INET sockets?

In Unix systems, sockets can communicate over two primary domains:


INET (TCP/IP) and Unix Domain (local IPC). Each uses a different address
structure.

Unix Domain Sockets: Used for inter-process communication on the


same host using file paths instead of IP addresses.

Structure:

struct sockaddr_un {

sa_family_t sun_family; // AF_UNIX

char sun_path[108]; // File path

};

INET Sockets: Used for network communication across machines via


TCP/IP.

Structure:

struct sockaddr_in {

sa_family_t sin_family; // AF_INET

in_port_t sin_port; // Port number

struct in_addr sin_addr;// IP address

};

Comparison:

Unix Domain
Feature INET Sockets
Sockets

Addressing File path IP + Port

Network
Use Local IPC
communication
Unix Domain
Feature INET Sockets
Sockets

Performanc Faster, no TCP/IP Slower, includes


e overhead routing

Example:

sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

struct sockaddr_un addr;

addr.sun_family = AF_UNIX;

strcpy(addr.sun_path, "/tmp/mysocket");

bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));

Q8. What is socketpair() and how can it be used for full-duplex


communication between two processes?

The socketpair() system call creates a pair of connected, unnamed sockets


for two-way communication between related processes (e.g., parent-
child).

Syntax:

int socketpair(int domain, int type, int protocol, int sv[2]);

 Typically used with AF_UNIX, SOCK_STREAM.

 sv[0] and sv[1] are two connected endpoints.

Use-case:

 Inter-process communication (IPC)

 Replacement for pipes with full-duplex capability

Example:

int sv[2];

socketpair(AF_UNIX, SOCK_STREAM, 0, sv);

if (fork() == 0) {

close(sv[0]);

write(sv[1], "Hello", 5);


} else {

close(sv[1]);

char buf[10];

read(sv[0], buf, 10);

Advantages:

 Full-duplex (bi-directional)

 Simpler than using pipe() for both directions

 Local-only, faster than network sockets

Q9. How do you implement a Unix Domain Datagram client/server,


and how is it different from Stream sockets?

Unix Domain Datagram Sockets use the SOCK_DGRAM type for


communication between processes on the same system. Unlike
SOCK_STREAM, they are connectionless and send fixed-sized messages.

Differences:

Featur Datagram Stream


e (SOCK_DGRAM) (SOCK_STREAM)

Type Connectionless Connection-oriented

Delivery Message-based Byte-stream

Orderin
Not guaranteed Guaranteed
g

Use- Continuous data


Simple messaging
case transfer

Server Code (Receiver):

int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);

struct sockaddr_un addr;

addr.sun_family = AF_UNIX;

strcpy(addr.sun_path, "/tmp/[Link]");
bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));

recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);

Client Code (Sender):

int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);

struct sockaddr_un addr;

addr.sun_family = AF_UNIX;

strcpy(addr.sun_path, "/tmp/[Link]");

sendto(sockfd, "Hello", 5, 0, (struct sockaddr*)&addr, sizeof(addr));

Use-cases:

 Local inter-process notifications

 Lightweight message exchanges

 Simple logging agents

Q10. What is a TCP Iterative Server, and how does it compare to a


Concurrent Server?

A TCP Iterative Server handles one client at a time using a single


process. It accepts a connection, processes it fully, then closes and
accepts the next.

Server Flow:

1. Create socket

2. Bind and listen

3. Accept client

4. Process and respond

5. Close connection and repeat

Example:

while (1) {

clientfd = accept(sockfd, ...);


handle_client(clientfd);

close(clientfd);

Comparison with Concurrent Server:

Iterative
Feature Concurrent Server
Server

Multiple
Clients One at a time
simultaneously

Complexit
Simple Needs fork/threads
y

Scalabilit
Limited High
y

Applications:

 Embedded systems

 Lightweight devices

 Simple services (e.g., Echo server)

Q11. What is a Preforked Server without locking around accept(),


and what issues can it cause?

In a preforked server, multiple child processes are created in advance to


handle incoming clients. Each child calls accept() on the same listening
socket.

Without Locking: When multiple processes call accept() simultaneously


without synchronization, a race condition occurs. Two or more processes
may attempt to accept the same connection.

Issues:

 Lost or delayed connections

 Unpredictable client handling

 Difficult debugging and maintenance


Solutions:

 File Locking: Use fcntl() or flock() around accept() section

 Accept Mutex: Use shared memory or semaphores

 Serialize with Master Process: Let the master accept, and pass
sockets to workers

Sample Lock Implementation:

flock(fd, LOCK_EX);

clientfd = accept(sockfd, ...);

flock(fd, LOCK_UN);

Q12. How can one process send file descriptors to another using
Unix domain sockets?

Descriptor passing allows sending open file descriptors between


processes using Unix Domain Sockets and the sendmsg() / recvmsg()
system calls.

Key Concepts:

 Uses control messages (cmsghdr) and SCM_RIGHTS

 Socket type: AF_UNIX, SOCK_STREAM or SOCK_DGRAM

Sender (Parent Process):

int fds[2];

socketpair(AF_UNIX, SOCK_STREAM, 0, fds);

int fd_to_send = open("[Link]", O_RDONLY);

struct msghdr msg = {0};

char buf[CMSG_SPACE(sizeof(int))];

struct iovec io = {.iov_base = "A", .iov_len = 1};

msg.msg_iov = &io; msg.msg_iovlen = 1;


msg.msg_control = buf;

msg.msg_controllen = CMSG_SPACE(sizeof(int));

struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);

cmsg->cmsg_level = SOL_SOCKET;

cmsg->cmsg_type = SCM_RIGHTS;

cmsg->cmsg_len = CMSG_LEN(sizeof(int));

memcpy(CMSG_DATA(cmsg), &fd_to_send, sizeof(int));

sendmsg(fds[0], &msg, 0);

Receiver (Child Process):

struct msghdr msg = {0};

char buf[CMSG_SPACE(sizeof(int))];

struct iovec io = {.iov_base = "B", .iov_len = 1};

msg.msg_control = buf;

msg.msg_controllen = sizeof(buf);

msg.msg_iov = &io; msg.msg_iovlen = 1;

recvmsg(fds[1], &msg, 0);

struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);

int received_fd = *(int *)CMSG_DATA(cmsg);

Common questions

Powered by AI

pselect() enhances signal handling in I/O multiplexing by allowing atomic signal masking, which avoids race conditions between handling I/O events and signals. Unlike select(), which does not handle signals safely and lacks microsecond-level timeout precision, pselect() includes a sigmask parameter enabling signals to be masked safely during the wait. This makes it more suitable for real-time systems and applications requiring reliable event-driven models . By addressing vulnerabilities like concurrent signal handling and providing nanosecond-level timeout precision, pselect() is preferred for systems needing to manage both socket events and asynchronous signal interrupts effectively .

The readv() and writev() system calls enhance I/O efficiency by allowing multiple buffers to be read or written in a single operation, minimizing the number of system calls and reducing user-kernel mode switch overhead . This scatter/gather I/O approach is ideal for applications like web servers where data is separated into headers and body segments or for protocols requiring a separation between metadata and payload. By consolidating data management in fewer operations, these calls improve performance, especially in high-throughput environments . The enhancements in performance and efficiency make them suitable for complex data processing scenarios requiring robust I/O management.

The shutdown() function in TCP socket programming allows for a controlled termination of a socket's communication in one direction (sending or receiving) without releasing resources, thus facilitating ongoing machine interaction and graceful service degradation. It can selectively disable certain communication channels (read, write, or both) using flags like SHUT_RD, SHUT_WR, or SHUT_RDWR . This contrasts with close(), which immediately closes the socket descriptor and frees all associated resources, making the socket unusable afterwards . Shutdown() enables finer control over communication cessation, essential for ensuring communication is gracefully and predictably finalized without abrupt disconnection, which might lead to data loss or error states in applications.

setsockopt() can manage read and write timeouts in socket programming by setting the SO_RCVTIMEO and SO_SNDTIMEO options, specifying how long recv() and send() operations should block before timing out. This involves configuring a timeval structure with a time in seconds and setting it via setsockopt(). Implementing socket timeouts prevents indefinite blocking during communication failures, enhances user experiences in real-time systems, and enables retry or fallback mechanisms, thus optimizing responsiveness and resilience against network delays or disconnections . If a timeout occurs, the related functions return with an error, allowing applications to handle such events intelligently.

A basic TCP Echo Server acts as an educational tool demonstrating TCP's bidirectional communication capabilities. It can be used for testing network communication where data sent to the server is echoed back to the client, allowing for simple debugging and learning scenarios . Limitations include handling only one client at a time due to its iterative nature, lack of concurrency, and error handling, which make it unsuitable for high-traffic systems . Despite these limitations, it serves as a foundational model for developing more complex server architectures with improved scalability and error management.

recv() and send() are more advantageous in scenarios requiring advanced socket-specific functionalities, such as using flags for controlling data transmission that are not available with the generic read() and write() functions. recv() and send() allow for operations like non-blocking or peeking into the socket buffer without removing data (using MSG_PEEK), providing greater flexibility and control over the data transmission process . These functions are ideal for complex socket applications where nuanced management of the transmission mode is crucial, while read() and write() suffice in simpler applications without the need for such explicit controls.

Operating a Preforked Server without synchronization mechanisms when calling accept() leads to race conditions where multiple processes might simultaneously try to accept the same client connection, causing lost or delayed connections, unpredictable handling, and complex debugging . Solutions involve implementing file locking using fcntl() or flock() for synchronized access during the accept() phase, utilizing an accept mutex that ensures serialized access to the listening socket, or employing a master process to handle accept() calls and distribute sockets to worker processes . These strategies ensure orderly client management and remove concurrency issues inherent in unsynchronized environments.

socketpair() is preferred over pipe() in scenarios requiring full-duplex communication (bi-directional) between two related processes, such as parent-child processes, where data needs to flow in both directions simultaneously . Unlike pipe(), which is half-duplex, socketpair() offers a simpler implementation for full-duplex communication using two connected, unnamed sockets. This makes it easier for processes to exchange messages bi-directionally without extra logic for handling each direction separately. Additionally, socketpair() provides local-only communication that is faster than using network sockets due to the lack of routing overhead . This efficiency and simplicity are beneficial in complex IPC where bidirectional data transfer is crucial.

Unix Domain Sockets use the sockaddr_un structure, identified by file paths for communication on the same host, whereas INET sockets use sockaddr_in for TCP/IP communications with IP addresses and port numbers . Unix Domain Sockets facilitate efficient inter-process communication (IPC) locally without TCP/IP overhead, leading to faster performance. They are primarily used in local-only applications where processes need to interact on the same machine. INET sockets, on the other hand, are used for network-level communications across different machines, allowing for broad connectivity over networks . These structural differences underline their specific applicability in local versus network-wide communication needs.

Unix Domain Datagram sockets, characterized by the SOCK_DGRAM type, offer connectionless communication with fixed-size message delivery, without guaranteed ordering, suitable for simple, lightweight message exchanges or notifications between processes on the same machine . Stream sockets, in contrast, are connection-oriented (SOCK_STREAM), ensuring byte-stream delivery with guaranteed ordering, making them ideal for continuous data transfer and scenarios requiring reliable session communication such as file transfers or complex data interactions . Datagram sockets excel in applications needing efficiency and low overhead, while Stream sockets are preferred when ensuring message integrity and order is vital.

You might also like