- Sockets: These are the fundamental building blocks for network communication. Think of them as virtual endpoints that allow different processes to exchange data.
- IP Addresses: These are unique identifiers for devices on a network, like a home address for your computer.
- Ports: These are virtual doorways on a computer, allowing different applications to listen for and receive data.
socket(): This function creates a new socket.bind(): This function assigns an address to a socket.listen(): This function puts the server socket into a listening state.accept(): This function accepts a connection from a client.connect(): This function establishes a connection to a server.send()andrecv(): These functions are used to send and receive data through the socket.
Let's dive into creating a daytime client-server program in C. This is a fantastic project for understanding network programming basics. We'll walk through the server and client code, explaining each part so you can get a solid grasp of how it all works. This guide aims to provide a clear and concise explanation, suitable for both beginners and those looking to refresh their knowledge of socket programming in C. By the end of this article, you'll be able to implement your own simple client-server application that serves the current date and time.
Understanding the Basics
Before we jump into the code, let's cover some fundamental concepts. At its core, a client-server program involves two entities: the server, which listens for incoming requests, and the client, which initiates those requests. They communicate using sockets, which are endpoints for sending and receiving data across a network. The server binds to a specific port and listens for connections. When a client connects, the server accepts the connection and handles the request. In our case, the request is simple: the client wants to know the current date and time. The server then sends the information back to the client, which displays it to the user.
Key components of this interaction include:
Knowing these terms and functions is crucial before diving deep into the code, so take a moment to familiarize yourself with them. We will use them extensively in our example.
Server-Side Code
Let's start with the server-side code. The server's job is to listen for incoming connections, accept them, get the current date and time, and send it back to the client. Here's a breakdown of the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 13
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char *message;
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// Binding the socket to the specified address and port
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// Listening for incoming connections
if (listen(server_fd, 3) < 0) {
perror("listen failed");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d\n", PORT);
// Accepting incoming connections
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept failed");
exit(EXIT_FAILURE);
}
// Get current time
time_t rawtime;
struct tm *timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
message = asctime(timeinfo);
// Sending the time to the client
send(new_socket, message, strlen(message), 0);
printf("Message sent to client: %s", message);
// Closing the socket
close(new_socket);
close(server_fd);
return 0;
}
This server code begins by including necessary header files for socket programming, such as stdio.h, stdlib.h, string.h, time.h, unistd.h, sys/socket.h, and netinet/in.h. These headers provide functions for input/output, standard library functions, string manipulation, time functions, POSIX operating system API, socket creation, and internet protocol family, respectively. We define the PORT number as 13, which is the standard port for the daytime service. The main() function initializes the server. It first creates a socket file descriptor using the socket() function, specifying the address family as AF_INET (IPv4), the socket type as SOCK_STREAM (TCP), and the protocol as 0 (default for TCP). If the socket creation fails, an error message is printed, and the program exits. Next, a sockaddr_in structure is defined to store the server address information, including the address family, IP address, and port number. The bind() function is then called to bind the socket to the specified address and port. This associates the socket with a specific IP address and port number, allowing clients to connect to the server. If binding fails, an error message is printed, and the program exits. The listen() function is called to put the server socket into a listening state, waiting for incoming connections. The second argument, 3, specifies the maximum number of pending connections that can be queued. If listening fails, an error message is printed, and the program exits. The server then enters a loop, continuously accepting incoming connections using the accept() function. This function blocks until a client attempts to connect. Once a connection is accepted, a new socket file descriptor is created for the connection. If accepting fails, an error message is printed, and the program exits. The current time is obtained using the time() and localtime() functions. The time is then formatted as a string using the asctime() function. The formatted time string is sent to the client using the send() function. The send() function sends the data over the socket to the connected client. Finally, the sockets are closed using the close() function, releasing the resources associated with them. The server then continues to listen for new connections.
Client-Side Code
Now, let's look at the client-side code. The client's job is to connect to the server, receive the date and time, and display it. Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 13
int main() {
int sock = 0, valread;
struct sockaddr_in serv_addr;
char buffer[1024] = {0};
// Creating socket file descriptor
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);
// Convert IPv4 and IPv6 addresses from text to binary form
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
printf("\nInvalid address/ Address not supported \n");
return -1;
}
// Connecting to the server
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
// Reading the time from the server
valread = read(sock, buffer, 1024);
printf("%s\n",buffer);
// Closing the socket
close(sock);
return 0;
}
This client code starts by including essential header files, similar to the server code. These headers facilitate socket programming, input/output operations, string manipulation, and network-related functions. The PORT number is defined as 13, matching the server's port. The main() function initializes the client. A socket file descriptor is created using the socket() function with the same parameters as the server. If socket creation fails, an error message is displayed, and the program exits. A sockaddr_in structure is defined to store the server address information, including the address family and port number. The inet_pton() function is used to convert the IP address from text to binary form. In this case, the IP address is "127.0.0.1", which represents the local loopback address. If the address conversion fails, an error message is displayed, and the program exits. The connect() function is called to establish a connection to the server. This function attempts to connect the socket to the server's address. If the connection fails, an error message is displayed, and the program exits. Once the connection is established, the read() function is used to read data from the server. The data is stored in a buffer. If reading fails, an error message is displayed, and the program may exit (though in this example, it doesn't explicitly handle read failures). The received data, which represents the current time, is printed to the console. Finally, the socket is closed using the close() function, releasing the resources associated with it.
Compiling and Running the Code
To compile and run these programs, you'll need a C compiler (like GCC) and a terminal. Here's how:
- Save the server code in a file named
server.cand the client code in a file namedclient.c. - Compile the server code:
gcc server.c -o server - Compile the client code:
gcc client.c -o client - Run the server:
./server - In a separate terminal, run the client:
./client
Make sure the server is running before you run the client. The client should then display the current date and time received from the server.
Error Handling and Improvements
The provided code examples are basic and lack robust error handling. In a real-world application, you'd want to add more comprehensive error checks. For instance, check the return values of read() and send() to handle cases where data is not fully sent or received. Additionally, consider adding timeouts to prevent the server from hanging indefinitely if a client disconnects unexpectedly. Another improvement would be to use non-blocking sockets and select() or poll() to handle multiple client connections concurrently.
Here are some specific error handling improvements to consider:
- Check return values: Always check the return values of system calls like
socket(),bind(),listen(),accept(),connect(),send(), andrecv(). A return value of -1 typically indicates an error, anderrnowill be set to indicate the specific error. - Handle
SIGPIPE: When a client disconnects unexpectedly, the server may receive aSIGPIPEsignal when attempting to send data to the disconnected socket. You can handle this signal by ignoring it or by usingsend()with theMSG_NOSIGNALflag. - Implement timeouts: Use
setsockopt()with theSO_RCVTIMEOandSO_SNDTIMEOoptions to set timeouts for receiving and sending data. - Log errors: Use
perror()orfprintf()to log error messages to a file or the console. This can help you diagnose problems and track down bugs.
Conclusion
Creating a daytime client-server program in C is an excellent way to learn about socket programming and network communication. This guide walked you through the basic steps of setting up a server and client, sending data between them, and displaying the results. Remember to enhance your code with proper error handling and explore more advanced techniques like non-blocking sockets for better performance and scalability. Keep practicing, and you'll become proficient in network programming in no time! This project provides a solid foundation for building more complex network applications.
Lastest News
-
-
Related News
Julius Randle NBA 2K21 Rating: How Good Was He?
Alex Braham - Nov 9, 2025 47 Views -
Related News
Decoding Oscou002639significadosc Sccivicsc: A Comprehensive Guide
Alex Braham - Nov 13, 2025 66 Views -
Related News
Nissan Altima 2024: News And Expectations For Brazil
Alex Braham - Nov 13, 2025 52 Views -
Related News
Liverpool Vs Everton: Reliving The Top 10 Epic Clashes
Alex Braham - Nov 9, 2025 54 Views -
Related News
Where To Watch: FC Utrecht Vs. Servette
Alex Braham - Nov 13, 2025 39 Views