Server
Server
Server
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
close(new_socket);
close(server_fd);
return 0;
}
===================================================================================
=======================
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE] = {0};
char *hello = "Hello from client";
// Create socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
close(sock);
return 0;
}
===================================================================================
====================
gcc server.c -o server
gcc client.c -o client
./server ./client
Server Code:
Client Code:
int main() {
int server_fd, new_socket, client_socket[MAX_CLIENTS], max_sd, sd, activity,
valread;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE];
fd_set readfds;
while (1) {
// Clear the socket set
FD_ZERO(&readfds);
break;
}
}
}
if (FD_ISSET(sd, &readfds)) {
// Check if it was for closing, and also read the incoming message
if ((valread = read(sd, buffer, BUFFER_SIZE)) == 0) {
// Somebody disconnected, get his details and print
getpeername(sd, (struct sockaddr *)&address, (socklen_t
*)&addrlen);
printf("Host disconnected, ip %s, port %d \n",
inet_ntoa(address.sin_addr), ntohs(address.sin_port));
close(server_fd);
return 0;
}
===================================================================================
====================
Initialization:
The server sets up a socket and binds it to a port, just like in the previous
example.
Client Handling:
When the server socket becomes readable, it means a new client is trying to
connect.
The server accepts the connection and adds the new client socket to the array.
Communication:
When a client socket becomes readable, the server reads the message.
If a client disconnects, the server closes the socket and removes it from the
array.
If a message is received, the server echoes it back to the client.
===================================================================================
======================
Now, the server can handle multiple clients concurrently, reading and writing to
each as needed.
This basic example demonstrates how to extend the server to handle multiple clients
in a single-threaded
manner using the select function. For more advanced handling, such as using
threads or asynchronous I/O,
further modifications would be needed.
To handle multiple clients more efficiently, we can use asynchronous I/O (non-
blocking I/O) with the `epoll` API, which is more scalable than `select` for a
large number of file descriptors. This approach is well-suited for high-performance
network servers.
Here’s an example of how to implement a server using `epoll` for asynchronous I/O
in C:
```c
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
while (1) {
// Wait for events
nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
close(server_fd);
close(epoll_fd);
exit(EXIT_FAILURE);
}
// Handle events
for (int n = 0; n < nfds; ++n) {
if (events[n].data.fd == server_fd) {
// Handle new connections
while ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t *)&addrlen)) != -1) {
set_nonblocking(new_socket);
ev.events = EPOLLIN | EPOLLET; // Edge-triggered mode
ev.data.fd = new_socket;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_socket, &ev) == -1)
{
perror("epoll_ctl: new_socket");
close(new_socket);
}
printf("New connection, socket fd is %d, ip is : %s, port : %d\
n",
new_socket, inet_ntoa(address.sin_addr),
ntohs(address.sin_port));
}
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("accept");
}
} else {
// Handle data from clients
int client_fd = events[n].data.fd;
int bytes_read;
while ((bytes_read = read(client_fd, buffer, BUFFER_SIZE)) > 0) {
buffer[bytes_read] = '\0';
printf("Client %d: %s\n", client_fd, buffer);
send(client_fd, buffer, bytes_read, 0);
}
if (bytes_read == 0) {
// Connection closed by client
printf("Client %d disconnected\n", client_fd);
close(client_fd);
} else if (bytes_read == -1 && errno != EAGAIN) {
perror("read");
close(client_fd);
}
}
}
}
close(server_fd);
close(epoll_fd);
return 0;
}
```
### Explanation
1. **Non-Blocking Sockets**:
- `set_nonblocking()` sets a file descriptor to non-blocking mode using `fcntl`.
2. **Epoll Setup**:
- `epoll_create1()` creates an epoll instance.
- `epoll_ctl()` adds the server socket to the epoll instance to monitor for
incoming connections.
3. **Event Loop**:
- `epoll_wait()` waits for events on the monitored file descriptors.
- When the server socket is readable, it means a new client connection is
incoming.
- For each new connection, the client socket is set to non-blocking mode and
added to the epoll instance.
- When a client socket is readable, the server reads the message, echoes it
back, or handles disconnection.
4. **Edge-Triggered Mode**:
- `EPOLLET` sets the socket to edge-triggered mode, meaning epoll will only
notify when the file descriptor transitions from not ready to ready.
```sh
gcc server.c -o server
gcc client.c -o client
```
```sh
./server
```
```sh
./client
```
===================================================================================
============================================================
1. **Message Length**: The first part of each message will indicate its length.
2. **Message Type**: We will define different message types for different kinds of
communication (e.g., chat messages, commands).
3. **Timestamp**: Each message will include a timestamp.
Here's how you can extend the previous example to include this protocol.
```c
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
// Format: <message_length>:<message_type>:<timestamp>:<message>
int len = snprintf(buffer, BUFFER_SIZE, "%d:%s:%s:%s", message_len, type,
timestamp, message);
send(sockfd, buffer, len, 0);
}
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE];
int epoll_fd, nfds;
struct epoll_event ev, events[MAX_EVENTS];
while (1) {
// Wait for events
nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_wait");
close(server_fd);
close(epoll_fd);
exit(EXIT_FAILURE);
}
// Handle events
for (int n = 0; n < nfds; ++n) {
if (events[n].data.fd == server_fd) {
// Handle new connections
while ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t *)&addrlen)) != -1) {
set_nonblocking(new_socket);
ev.events = EPOLLIN | EPOLLET; // Edge-triggered mode
ev.data.fd = new_socket;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_socket, &ev) == -1)
{
perror("epoll_ctl: new_socket");
close(new_socket);
}
printf("New connection, socket fd is %d, ip is : %s, port : %d\
n",
new_socket, inet_ntoa(address.sin_addr),
ntohs(address.sin_port));
}
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("accept");
}
} else {
// Handle data from clients
int client_fd = events[n].data.fd;
int bytes_read;
while ((bytes_read = read(client_fd, buffer, BUFFER_SIZE)) > 0) {
buffer[bytes_read] = '\0';
handle_message(client_fd, buffer);
send_message(client_fd, buffer, "RESPONSE");
}
if (bytes_read == 0) {
// Connection closed by client
printf("Client %d disconnected\n", client_fd);
close(client_fd);
} else if (bytes_read == -1 && errno != EAGAIN) {
perror("read");
close(client_fd);
}
}
}
}
close(server_fd);
close(epoll_fd);
return 0;
}
```
```c
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <time.h>
// Format: <message_length>:<message_type>:<timestamp>:<message>
int len = snprintf(buffer, BUFFER_SIZE, "%d:%s:%s:%s", message_len, type,
timestamp, message);
send(sockfd, buffer, len, 0);
}
printf("Received message:\n");
printf(" Type: %s\n", message_type);
printf(" Timestamp: %s\n", timestamp);
printf(" Message: %s\n", message);
}
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUFFER_SIZE];
// Create socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
memset(buffer, 0, BUFFER_SIZE);
int valread
close(sock);
return 0;
}
```
### Explanation
1. **Message Protocol**:
- Each message sent or received follows the format
`<message_length>:<message_type>:<timestamp>:<message>`.
2. **Non-Blocking Sockets**:
- Both server and client sockets are set to non-blocking mode to handle
asynchronous I/O efficiently.
3. **Timestamp Function**:
- A helper function generates a timestamp for each message.
4. **Send and Handle Messages**:
- `send_message()` formats and sends messages following the protocol.
- `handle_message()` parses and displays received messages.
5. **Edge-Triggered Epoll**:
- The server uses `epoll` in edge-triggered mode (`EPOLLET`) to handle high-
performance I/O.
```sh
gcc server.c -o server
gcc client.c -o client
```
```sh
./server
```
```sh
./client
```
This implementation introduces a basic protocol with message length, type, and
timestamp, enhancing communication structure
and readability for both the server and clients. The use of `epoll` ensures
efficient handling of multiple clients asynchronously.
===================================================================================
==================================================
When it comes to embedded systems, particularly for switches and other networking
hardware, there are several protocols that could be suitable depending on the
specific requirements of the system. Here are a few options, along with a brief
overview and example implementations:
MQTT is a lightweight messaging protocol designed for small sensors and mobile
devices. It's suitable for embedded systems due to its low bandwidth usage and
support for reliable communication.
**Server (Broker)**:
```c
// Example using an MQTT library such as Eclipse Paho
#include "MQTTClient.h"
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return rc;
}
```
**Client**:
```c
#include "MQTTClient.h"
int ch;
do {
ch = getchar();
} while(ch!='Q' && ch != 'q');
MQTTClient_disconnect(client, 10000);
MQTTClient_destroy(&client);
return rc;
}
```
CoAP is a specialized web transfer protocol for use with constrained nodes and
networks in the Internet of Things (IoT). It's suitable for embedded systems due to
its simplicity and efficiency.
**Server**:
```c
// Example using libcoap (CoAP library)
#include <coap2/coap.h>
int main() {
coap_context_t* coap_context;
coap_address_t listen_address;
coap_resource_t* resource;
coap_startup();
coap_context = coap_new_context(NULL);
if (!coap_context) {
return EXIT_FAILURE;
}
coap_address_init(&listen_address);
listen_address.addr.sin.sin_family = AF_INET;
listen_address.addr.sin.sin_addr.s_addr = INADDR_ANY;
listen_address.addr.sin.sin_port = htons(5683);
while (1) {
coap_io_process(coap_context, COAP_IO_WAIT);
}
coap_free_context(coap_context);
coap_cleanup();
return 0;
}
```
**Client**:
```c
// Example using libcoap (CoAP library)
#include <coap2/coap.h>
int main() {
coap_context_t* coap_context;
coap_address_t dst;
coap_session_t* session;
coap_pdu_t* request;
static const coap_uri_t uri = { COAP_URI_SCHEME_COAP, COAP_DEFAULT_PORT, 0,
(const uint8_t*)"example", 7 };
coap_startup();
coap_context = coap_new_context(NULL);
if (!coap_context) {
return EXIT_FAILURE;
}
coap_address_init(&dst);
dst.addr.sin.sin_family = AF_INET;
dst.addr.sin.sin_port = htons(5683);
inet_pton(AF_INET, "127.0.0.1", &dst.addr.sin.sin_addr);
request = coap_new_pdu(session);
request->type = COAP_MESSAGE_CON;
request->tid = coap_new_message_id(session);
request->code = COAP_REQUEST_GET;
coap_add_option(request, COAP_OPTION_URI_PATH, uri.path.length, uri.path.s);
coap_register_response_handler(coap_context, response_handler);
coap_send(session, request);
coap_io_process(coap_context, COAP_IO_WAIT);
coap_session_release(session);
coap_free_context(coap_context);
coap_cleanup();
return 0;
}
```
This example assumes you have installed the `protobuf-c` library and the Protocol
Buffers compiler.
message ExampleMessage {
string type = 1;
string timestamp = 2;
string message = 3;
}
```
**Server**:
```c
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "message.pb-c.h"
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
uint8_t buffer[BUFFER_SIZE];
if (message == NULL) {
fprintf(stderr, "error unpacking incoming message\n");
close(new_socket);
close(server_fd);
exit(EXIT_FAILURE);
}
example_message__free_unpacked(message, NULL);
close(new_socket);
close(server_fd);
return 0;
}
```
**Client**:
```c
// client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "message.pb-c.h"
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
uint8_t buffer[BUFFER_SIZE];
// Create socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
### Summary
Each protocol has its strengths and can be chosen based on specific requirements
such as latency, bandwidth, and complexity of the embedded system.
===================================================================================
=====================================================