Network Programming Unit-1 Part -2 ,Socket Programming
Network Programming Unit-1 Part -2 ,Socket Programming
1. What is a Socket?
Sockets are created using an IP address and a port number. The IP address identifies the device on the
network, while the port number identifies a specific service or application on that device.
2. Types of Sockets
- It is suitable for applications that require guaranteed delivery, such as web browsers, email clients,
and file transfer protocols.
- It is faster but less reliable than TCP, making it ideal for applications like online gaming, video
streaming, and real-time voice communication.
3. Basic Concepts
- Client-Server Model:
- The server listens for incoming connections on a specific port, while the client initiates the connection
to the server.
- Binding:- The server binds its socket to a specific IP address and port, making it available for client
connections.
- Listening:- After binding, the server listens for incoming client connections.
- Accepting: - When a client tries to connect, the server accepts the connection and establishes a
communication link.
- Sending and Receiving Data: - Once the connection is established, data can be sent and received
between the client and server using read and write operations.
4. Example in Python
Here’s a simple example of a TCP server and client in Python using the `socket` module.
Server:
```python
import socket
# Create a socket object
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Accept a connection
client_socket, addr = server_socket.accept()
print(f"Connected to {addr}")
# Send a response
client_socket.send(b"Hello, Client!")
Client:
```python
import socket
client_socket.connect(('localhost', 12345))
# Send data
client_socket.send(b"Hello, Server!")
# Receive a response
data = client_socket.recv(1024)
print(f"Received: {data.decode()}")
client_socket.close()
- Web Servers:Socket programming forms the basis of web servers that handle HTTP requests and send
responses to clients (web browsers).
- Chat Applications: Real-time messaging between users can be implemented using sockets.
- IoT Devices: Sockets allow communication between IoT devices, such as smart home systems.
6. Key Considerations
- Error Handling: Network communication can fail due to various reasons, so proper error handling is
essential.
Socket programming is a powerful tool for enabling communication between devices over a network.
Understanding the basics of sockets, including the client-server model, and knowing how to implement
simple socket programs, opens the door to building various networked applications.
Creating sockets is an essential part of network programming, allowing communication between
devices over a network. Here’s a breakdown of the basics of creating sockets in Python:
First, you need to import the socket module, which provides low-level networking interfaces.
```python
import socket
You can create a socket object using the `socket()` function. You need to specify the address family
(IPv4 or IPv6) and the socket type (TCP or UDP).
```python
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
3. Bind the socket to an address: -For a server, you need to bind the socket to an IP address and port.
This allows the socket to listen for incoming connections on the specified port.
```python
s.bind(server_address)
4. Listen for incoming connections (TCP): If you're using TCP, you'll need to put the socket in listening
mode so it can accept incoming connections.
```python
print("Server is listening...")
```
```python
print(f"Connected by {addr}")
```
You can now send and receive data through the socket. Here’s how to do it:
- TCP:
```python
# Receive data
data = conn.recv(1024)
print(f"Received: {data.decode()}")
# Send data
conn.sendall(b"Hello, client!")
```
- UDP:
```python
# Receive data
# Send data
```
```python
conn.close() # For TCP
- UDP (User Datagram Protocol):Unreliable, connectionless protocol. Suitable for applications where
speed is more critical than reliability (e.g., video streaming, gaming).
```c
if (pid == 0) {
// Child process
} else {
// Parent process
```
```c
pthread_t thread;
- Used in functions like `getuid()` and `getgid()` to manage user and group permissions.
- Example:
```c
```
- Represents file permissions and types (e.g., read, write, execute permissions).
- Example:
```c
mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR; // Owner can read, write, and execute
mkdir("mydir", mode);
```
- Example:
```c
```
- Example:
```c
```
- Similar to `size_t`, but signed. Used for functions that can return negative values (e.g., `read()` and
`write()`).
- Example:
```c
```
8. `time_t` (Time):
- Example:
```c
```
- Example:
```c
// Do something
- Example:
```c
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
```
```c
stat("file.txt", &file_info);
- Example:
```c
tv.tv_sec = 1; // 1 second
tv.tv_usec = 500000; // 500 milliseconds
```
Common Uses:
- Inter-process communication (IPC): Data types like `pid_t`, `uid_t`, and `gid_t` are used in managing
processes and their permissions.
- File handling: Types like `mode_t`, `off_t`, and `struct stat` are critical in manipulating and querying file
systems.
- Timing and signals: Data types like `time_t`, `sigset_t`, and `struct timeval` are crucial in handling
signals and measuring or managing time intervals.
POSIX data types are foundational for writing portable and reliable applications that interact with the
underlying operating system and its resources.
A socket address is a combination of an IP address and a port number that uniquely identifies a
specific endpoint in a network. In networking, it allows communication between devices by specifying
where data should be sent or received. Here's a breakdown of the components:
1. IP Address:This is the numerical label assigned to each device connected to a network. It can be an
IPv4 address (e.g., 192.168.1.1) or an IPv6 address (e.g., 2001:0db8:85a3:0000:0000:8a2e:0370:7334).
2. Port Number:This is a 16-bit number that identifies a specific process or service on a device. For
example, port 80 is commonly used for HTTP web traffic, and port 443 is used for HTTPS.
When combined, the IP address and port number form the **socket address**. For example:
- `192.168.1.1:80`
Usage in Programming
In programming, socket addresses are used in socket programming to establish connections between a
client and a server. A socket is bound to a socket address and can send and receive data over the
network.
Assigning an address to a socket involves binding the socket to a specific IP address and port
number. This process is crucial in network programming because it allows a server to listen for incoming
connections on a particular network interface.
```python
import socket
# Create a socket object
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Define the IP address and port number to bind the socket to
host = '127.0.0.1' # Localhost
port = 12345 # Arbitrary non-privileged port
# Bind the socket to the address
s.bind((host, port))
# Now the socket is bound to the specified IP address and port number
print(f"Socket bound to {host}:{port}")
# After binding, you can make the socket listen for incoming connections (for a server)
s.listen(5)
print("Socket is now listening for incoming connections...")
# Accept an incoming connection (this will block until a connection is received)
conn, addr = s.accept()
print(f"Got a connection from {addr}")
# Close the connection
conn.close()
s.close()
```
main points:
1. Create the Socket: The `socket.socket()` function creates a socket object. The parameters specify the
address family (IPv4) and the socket type (TCP).
2. Bind the Socket: The `bind()` method assigns the socket to a specific IP address and port number. The
argument is a tuple `(host, port)`.
3. Listen for Connections: For server sockets, after binding, you call `listen()` to make the socket listen
for incoming connections.
4. Accept Connections: The `accept()` method waits for an incoming connection. Once a connection is
established, it returns a new socket object representing the connection and the address of the client.
Common Errors:
- Address already in use: If the port is already occupied, you may encounter this error. Choose a
different port or wait for the existing one to be released.
- Permission Denied: Ports below 1024 are privileged and usually require administrative rights.
Java socket programming is a mechanism that allows two machines (or processes) to
communicate with each other over a network using sockets. Sockets provide a way for a client and a
server to communicate by sending and receiving data over TCP/IP or UDP/IP protocols.
1. Socket: A socket is an endpoint for communication between two machines. It’s like a telephone
connection where both parties need to have a receiver to communicate.
2. ServerSocket: This class is used on the server side. It waits for incoming client connections and then
opens a communication line.
3. Socket: On the client side, the `Socket` class is used to initiate a connection to the server.
4. InputStream/OutputStream: These are used to read data from the socket and send data to the socket,
respectively.
1. Server Side:
- Once a client connects, `accept()` returns a `Socket` object representing the connection.
- Use `InputStream` and `OutputStream` of the `Socket` to read and write data.
2. Client Side:
- Create a `Socket` object to connect to the server using the server’s IP address and port.
- Use `InputStream` and `OutputStream` of the `Socket` to communicate with the server.
Example Code
Server Side
```java
import java.io.*;
import java.net.*;
public class Server {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(12345)) {
System.out.println("Server is listening on port 12345");
while (true) {
try (Socket socket = serverSocket.accept()) {
System.out.println("New client connected");
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
OutputStream output = socket.getOutputStream();
PrintWriter writer = new PrintWriter(output, true);
String message;
while ((message = reader.readLine()) != null) {
System.out.println("Received: " + message);
writer.println("Echo: " + message);
}
} catch (IOException ex) {
System.out.println("Server exception: " + ex.getMessage());
}
}
} catch (IOException ex) {
System.out.println("Server exception: " + ex.getMessage());
}
}
}
```
Client Side
```java
import java.io.*;
import java.net.*;
do {
System.out.print("Enter message: ");
text = consoleReader.readLine();
writer.println(text);
- The server runs first and listens for connections on a specific port (e.g., `12345`).
- The client connects to the server using the server's IP address and the same port.
- Once connected, the client can send messages to the server, and the server echoes them back.
- Multithreading: In real-world applications, servers are usually multithreaded to handle multiple client
connections simultaneously.
- Exception Handling: Proper exception handling is critical in network programming to ensure that
connections are properly closed in case of errors.
Thread programming involves the use of multiple threads within a single process to perform
concurrent operations. This is common in modern applications where tasks can be broken down into
smaller units that can run in parallel, improving performance and responsiveness.
1. Thread: A lightweight process that can be managed independently by a scheduler. Threads share the
same memory space but execute independently.
- Concurrency: Multiple threads make progress within the same application, possibly interleaving
execution. It doesn't necessarily mean the tasks are running simultaneously, but they appear to be.
- Parallelism: Multiple threads are executed simultaneously, typically on different processors or cores.
3. Thread Lifecycle:
- Runnable: The thread is ready to run and waiting for CPU time.
- Timed Waiting: The thread is waiting for a specific amount of time before becoming runnable again.
4. Thread Synchronization:
- Mutex (Mutual Exclusion): Ensures that only one thread accesses a resource at a time.
- Monitors: A synchronization construct that allows threads to have both mutual exclusion and the
ability to wait for a certain condition to become true.
5. Thread Safety: Ensuring that the code behaves correctly when multiple threads access shared
resources. Common techniques include using locks, avoiding shared mutable data, and utilizing thread-
safe data structures.
6. Deadlock: A situation where two or more threads are blocked forever, waiting for each other to
release resources. Avoidance strategies include:
- Lock ordering.
- Using timeouts.
7. Race Condition: Occurs when the behavior of a software system depends on the relative timing of
events, such as thread execution, leading to unpredictable results.
8. Thread Pools: A collection of pre-instantiated reusable threads that can be used to perform tasks,
reducing the overhead of creating and destroying threads frequently.
- C++: Uses the `<thread>` library in modern C++ (C++11 and later).
Example in Python
```python
import threading
def print_numbers():
for i in range(5):
print(i)
def print_letters():
print(letter)
# Creating threads
t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_letters)
# Starting threads
t1.start()
t2.start()
t1.join()
t2.join()
print("Done!")
```
Berkeley Sockets, also known as BSD sockets, are an Application Programming Interface (API) that
provides a way for programs to communicate over a network using Internet Protocol (IP) or UNIX
domain sockets. Developed as part of the Berkeley Software Distribution (BSD) UNIX in the early 1980s,
this API allows for inter-process communication (IPC) and is widely used in networking applications,
especially for the development of TCP/IP-based services.
- Stream sockets (SOCK_STREAM): Used for reliable, connection-oriented services like TCP.
- Datagram sockets (SOCK_DGRAM): Used for connectionless services like UDP, where data is sent in
discrete packets.
A typical server-side implementation using Berkeley Sockets involves creating a socket, binding it to a
network address, listening for incoming connections, and then accepting and handling those
connections. On the client side, a socket is created, and a connection is initiated to a server, after which
data can be exchanged until the connection is closed.
This API has been foundational in the development of networked applications and continues to be
relevant in modern computing, despite the availability of newer networking frameworks.
For more detailed information, you can refer to sources such as Wikipedia and technical guides on
socket programming.
```c
struct sockaddr_in {
};
struct in_addr {
};
- `sin_port`: Stores the port number, typically in network byte order (use `htons()` and `ntohs()` for
conversion).
- `sin_zero`: Padding to make the structure the same size as `struct sockaddr`.
struct sockaddr_in6 {
};
struct in6_addr {
};
```
- `sin6_scope_id`: Specifies the scope of the address, typically used for link-local addresses.
```c
struct sockaddr {
};
This structure is typically cast to the specific structure (e.g., `struct sockaddr_in` or `struct
sockaddr_in6`) depending on the address family.
Conversion Functions
- `htons()` and `ntohs()`: Convert between host and network byte order for port numbers.
- `inet_pton()`: Convert IP address from text to binary form (supports both IPv4 and IPv6).
- `inet_ntop()`: Convert IP address from binary to text form (supports both IPv4 and IPv6).
Here’s a simple example of how to initialize a socket address structure for an IPv4 address:
```c
addr.sin_family = AF_INET;
```
This structure can then be used in socket functions like `bind()`, `connect()`, and `sendto()`.
Understanding these structures is crucial for working with network sockets in C/C++ and many other
programming languages.
Socket address structures are used in network programming to hold information about the
address of a socket. These structures differ slightly depending on the protocol (IPv4, IPv6, etc.). Here are
some commonly used socket address structures:
1. `struct sockaddr`
- Description: This is the generic socket address structure. It's used as a base type, and specific
structures (like `sockaddr_in` for IPv4) are usually cast to/from this type when passing addresses to
functions.
- Definition:
```c
struct sockaddr {
unsigned short sa_family; // Address family (AF_INET, AF_INET6, etc.)
char sa_data[14]; // Protocol-specific address data
};
```
2. `struct sockaddr_in` (IPv4)
- Definition:
```c
struct sockaddr_in {
unsigned short int sin_port; // Port number (in network byte order)
};
- Components:
- `sin_port`: The port number, usually converted to network byte order using `htons`.
- `sin_addr`: Holds the IPv4 address, typically set using `inet_pton` or `inet_aton`.
- Definition:
``` ```c
struct in_addr {
};
4. `struct sockaddr_in6` (IPv6)
- Definition:
```c
struct sockaddr_in6 {
};
- Components:
- `sin6_port`: The port number, usually converted to network byte order using `htons`.
- Definition:
```c
struct in6_addr {
};
- Description: This structure is used for Unix domain sockets, which allow communication between
processes on the same host.
- Definition:
```c
struct sockaddr_un {
};
- Components:
- `sun_path`: A null-terminated string representing the file system path to the socket.
Usage Examples
Here is an example of how you might initialize a `sockaddr_in` structure for an IPv4 socket:
```c
server_address.sin_family = AF_INET;
These structures are used in various networking functions, such as `bind()`, `connect()`, `sendto()`, and
`recvfrom()`, to specify the socket's address.
Byte manipulation and address conversion are common tasks in systems programming,
network programming, and various low-level operations. Here, I'll provide examples of byte
manipulation and address conversion in Python.
```python
# Shift the number to the right by 8 * byte_position and mask with 0xFF to get the byte
```
```python
# Clear the byte at byte_position, then OR with the new byte value
```
```python
return number
```
Address Conversion Functions
```python
import socket
import struct
def ipv4_to_int(ipv4_str):
packed_ip = socket.inet_aton(ipv4_str)
```
```python
def int_to_ipv4(ipv4_int):
```
```python
def mac_to_bytes(mac_str):
```
```python
def bytes_to_mac(byte_str):
```
Example Usage
- Extract and set byte:
```python
num = 0x12345678
byte_pos = 2
new_byte_val = 0xAB
```
```python
ipv4_str = "192.168.1.1"
ipv4_int = ipv4_to_int(ipv4_str)
ipv4_str_back = int_to_ipv4(ipv4_int)
```
These functions can be extended for various use cases, and can be adapted for different types of
address manipulations or byte operations.
In computer networking, a socket is one endpoint of a two-way communication link between two
programs running on a network. The "Elementary Socket System Calls" are the basic functions
provided by an operating system (typically in C) for network communication using sockets. These system
calls allow you to create, configure, and manage sockets for communication between different systems
or processes. Below are the primary elementary socket system calls:
1. socket()
- Syntax:
```c
```
- Parameters:
- `domain`: Specifies the communication domain (e.g., `AF_INET` for IPv4, `AF_INET6` for IPv6).
- `type`: Specifies the type of communication (e.g., `SOCK_STREAM` for TCP, `SOCK_DGRAM` for
UDP).
- `protocol`: Usually set to 0, which lets the system choose the appropriate protocol.
2. bind()
- Syntax:
```c
```
- Parameters:
3. listen()
- Purpose: Marks the socket as a passive socket (i.e., it will be used to accept incoming connection
requests).
- Syntax:
```c
```
- Parameters:
4. accept()
- Syntax:
```c
```
- Parameters:
- `addr`: A pointer to a `sockaddr` structure where the address of the connecting client will be stored.
5. connect()
- Syntax:
```c
```
- Parameters:
6. recv()
- Syntax:
```c
```
- Parameters:
- `buf`: A pointer to the buffer where the received data will be stored.
- `flags`: Flags for receiving data (e.g., `MSG_DONTWAIT` for non-blocking mode).
7. close()
- Syntax
```c
```
- Parameters:
8. `fork()`
- Purpose: `fork()` is a system call used to create a new process, called the child process, by duplicating
the calling process, known as the parent process. After a `fork()` call, both the parent and the child
process continue executing from the point where the `fork()` was called.
- Return Value:
- `fork()` is often used in socket programming to handle multiple clients. After accepting a connection
on a socket, a server might `fork()` a child process to handle the communication with that client while
the parent process continues to listen for new connections.
- Purpose: The `exec()` family of functions replaces the current process image with a new process image.
Essentially, after calling `exec()`, the process that called it is replaced by the new program specified in
the `exec()` call.
- Variations:
- Each variation differs in how arguments and environment variables are passed to the new process.
- In a socket programming context, after a server `fork()`s a child process, the child might use `exec()` to
run a different program to handle the client request. For example, after accepting a connection, the
server might `fork()`, and the child process might `exec()` a program to handle the client's request.
1. Socket Creation:The server creates a socket using the `socket()` system call.
2. Bind: The socket is bound to an IP address and port using the `bind()` system call.
3. Listen:The server listens for incoming connections using the `listen()` system call.
4. Accept: When a client connects, the server accepts the connection using the `accept()` system call.
5. Fork: The server `fork()`s a new process. The parent process returns to `accept()` more connections,
while the child process handles the connected client.
6. Exec: The child process might use an `exec()` call to replace itself with a different program to handle
the client communication.
These system calls form the foundation of network programming and are used to create both client and
server applications.
TCP Ports
TCP (Transmission Control Protocol) is one of the core protocols of the Internet Protocol (IP) suite. It is
responsible for establishing connections between devices on a network and ensuring reliable data
transfer.
Ephemeral ports are temporary ports assigned by a client machine to make a connection to a
server. Once the session is complete, the port is released and can be reassigned. These ports are
typically used for the client's side of a connection.
Use Case: When a client initiates a TCP connection to a server (e.g., a web browser connecting to a
web server), the client will be assigned an ephemeral port, and the server will listen on a well-known or
registered port (like port 80 for HTTP).
1. Reverse SSH Tunneling: - In reverse SSH tunneling, a remote server opens a port on the local
machine to allow a connection from the remote side. This technique is used to bypass firewalls or allow
access to systems that are behind NAT or firewalls.
2. Reverse Proxy: - In a reverse proxy, incoming requests are handled by an intermediary server, which
forwards them to the appropriate backend server. The reverse proxy server typically listens on a public
port, and the backend servers listen on internal ports.
3. Port Redirection: - Sometimes, services listen on non-standard ports for security or other reasons,
and a firewall or router might "reverse" the port by forwarding traffic from a well-known port to the
internal service on a different port.
These concepts illustrate how ports can be used in non-standard or reversed ways to facilitate specific
network scenarios.
Berkeley sockets, often simply called sockets, are an API used for inter-process communication (IPC) and
network communication in Unix-like systems. They provide a standardized interface for applications to
send and receive data over a network, regardless of the underlying protocol (e.g., TCP or UDP).
- Non-blocking I/O: In non-blocking I/O, socket operations return immediately without waiting for the
operation to complete. If the operation would block (e.g., no data is available to read), the function
returns an error (e.g., `EWOULDBLOCK`). This allows the process to continue executing other tasks and
check back later.
2. Asynchronous I/O:
- In asynchronous I/O, the process does not need to wait for the I/O operation to complete. Instead,
the operation is initiated, and the system notifies the process when the operation has completed (e.g.,
through signals, callbacks, or by waking up the process).
- In the context of Berkeley sockets, asynchronous I/O can be implemented using mechanisms like
`select()`, `poll()`, or more advanced interfaces like `epoll` (Linux) or `kqueue` (BSD). These allow the
process to monitor multiple sockets for events (e.g., data available to read) and handle them as they
occur.
3. I/O Multiplexing:
- I/O multiplexing is a technique that allows a single process to monitor multiple file descriptors
(including sockets) to see if I/O is possible on any of them. This is crucial for handling multiple
simultaneous connections without spawning a new thread or process for each connection.
- `select()`: Allows a program to monitor multiple file descriptors (including sockets) for readability,
writability, or errors. It uses a fixed-size bitmask to represent file descriptors, which can be a limitation
for a large number of connections.
- `poll()`:Similar to `select()`, but uses an array of structures rather than a bitmask, allowing it to
handle a larger number of file descriptors.
- `epoll()` (Linux) / `kqueue()` (BSD):More advanced and scalable methods for I/O multiplexing. These
interfaces allow efficient monitoring of a large number of file descriptors by notifying the process only
when events of interest occur, rather than requiring constant polling.
- Resource Efficiency: Non-blocking and event-driven models use fewer system resources (e.g., memory,
CPU) compared to multi-threading.
- Responsiveness: The application can remain responsive by not being blocked on I/O operations.
1. `select()` Function
The `select()` function allows a program to monitor multiple file descriptors (e.g., sockets) to see if any
of them are ready for I/O operations (e.g., reading or writing). It's useful in situations where you want to
wait for any one of several file descriptors to become ready.
Syntax:
```c
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
```
- `nfds`: The highest-numbered file descriptor in any of the sets, plus one.
- `timeout`: Specifies the maximum time to wait for an event. If `NULL`, `select()` can block indefinitely.
2. `poll()` Function
The `poll()` function is more modern and flexible compared to `select()`. It can handle a larger number of
file descriptors and does not require the highest-numbered file descriptor to be passed, making it more
scalable.
Syntax:
```c
```
- `fds`: An array of `pollfd` structures, where each structure represents a file descriptor to monitor.
- `timeout`: The time to wait in milliseconds. If `timeout` is `-1`, `poll()` blocks indefinitely.
- Efficiency: `select()` requires you to calculate the highest file descriptor number and rebuild the file
descriptor sets on each call, whereas `poll()` does not.
- Timeout Precision: Both functions support timeouts, but the precision and resolution might differ
slightly, depending on the system implementation.
For very large-scale systems, modern alternatives like `epoll` (Linux) or `kqueue` (BSD) are often used
instead of `select()` or `poll()`.
1. `signal` Function:-The `signal` function is used to set up a signal handler, which is a function that will
be executed when a specific signal is received by the process.
Prototype:
```c
```
- `func`: The signal handler function or one of the predefined constants (`SIG_IGN` to ignore the signal,
`SIG_DFL` to use the default signal handler).
Example Usage:
```c
#include <signal.h>
#include <stdio.h>
int main() {
signal(SIGINT, handle_sigint);
return 0; }
- The `signal` function provides a simple way to handle signals but may have portability issues across
different systems.
- For more robust signal handling, it's often recommended to use the `sigaction` function.
2. `fcntl` Function:
The `fcntl` (file control) function performs various operations on file descriptors. It's a powerful tool
used for tasks like manipulating file descriptor flags, locking files, and more.
Prototype:
```c
```
- `cmd`: The command that specifies the operation (e.g., `F_GETFD`, `F_SETFD`, `F_SETFL`, `F_GETFL` for
file descriptor flags).
Key Points:
- `fcntl` is commonly used for file locking (`F_SETLK`, `F_GETLK`) and adjusting file descriptor flags.
- It provides more flexibility than simpler I/O operations and is essential in scenarios like non-blocking
I/O and file locks.
- `fcntl`: Performs operations on file descriptors, like setting flags or locking files.
If you meant a different function by "fentl," feel free to clarify, and I can provide additional information!
Server Program
```python
import socket
def server_program():
host = socket.gethostname()
server_socket.bind((host, port))
server_socket.listen(2)
while True:
data = conn.recv(1024).decode()
if not data:
break
if __name__ == '__main__':
server_program()
```
```python
import socket
def client_program():
data = client_socket.recv(1024).decode()
if __name__ == '__main__':
client_program()
```
How it Works:
1. Server:
- Once connected, it receives data from the client, processes it, and sends a response back.
2. Client:
- The process continues until the client sends the message 'bye', which terminates the connection.
- Run the server program first. This will make the server start listening for connections.
- Then, run the client program, which will connect to the server and start sending messages.
This example uses basic socket programming with a TCP connection. It can be expanded with more
features like error handling, multi-client support, and threading for handling multiple connections
simultaneously.
The UNIX domain protocol, also known as the UNIX domain socket, is a communication protocol
that allows data exchange between processes on the same host (i.e., within the same operating system).
Unlike network sockets that use IP for inter-process communication over a network, UNIX domain
sockets provide efficient, low-latency communication within the local system.
1. Local Communication: UNIX domain sockets are used for communication between processes on the
same host machine, bypassing the need for networking stacks.
2. Socket Types:
- Stream Sockets (SOCK_STREAM): Provide a reliable, connection-oriented byte stream, similar to TCP
sockets.
- Datagram Sockets (SOCK_DGRAM): Support connectionless, unreliable message-passing, similar to
UDP sockets.
3. Addressing: Instead of using IP addresses and port numbers, UNIX domain sockets use file system
paths as addresses. The path refers to a special file in the file system (often located in the `/tmp`
directory) that represents the socket.
4. Efficiency: Since UNIX domain sockets do not involve network protocols or interfaces, they offer faster
data transmission between processes on the same machine compared to network sockets.
5. Security: Access to UNIX domain sockets can be controlled using file system permissions, which adds
an additional layer of security.
6. Use Cases: Commonly used for communication between different components of software systems
on a single host, such as communication between a web server (e.g., Nginx) and an application server
(e.g., a Python web application).
Example Usage:
In a typical UNIX domain socket application, a server creates a socket, binds it to a file path, and listens
for connections. A client then connects to this socket by referencing the same file path.
```python
import socket
import os
# Server side
server_address = '/tmp/my_socket'
if os.path.exists(server_address):
os.unlink(server_address)
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server.bind(server_address)
server.listen(1)
print('Waiting for a connection...')
connection, client_address = server.accept()
try:
print('Connection from', client_address)
while True:
data = connection.recv(1024)
if data:
print('Received:', data.decode())
connection.sendall(data)
else:
break
finally:
connection.close()
server.close()
```python
# Client side
client.connect('/tmp/my_socket')
try:
client.sendall(b'Hello, server!')
data = client.recv(1024)
print('Received:', data.decode())
finally:
client.close()
```
In this example, the server waits for connections on a socket file, and the client connects to it and
exchanges data.