Explanation of Opserver
Explanation of Opserver
string.h: This header provides various string manipulation functions, like strlen, strcpy, and strcat.
stdlib.h: This header provides general utility functions, such as memory allocation (malloc, free) and
conversion functions (atoi, atof).
unistd.h: This header provides access to various POSIX operating system API functions, including low-
level I/O operations (such as read and write) and system calls.
arpa/inet.h: This header provides definitions for internet operations, like converting internet addresses
between binary and text form (inet_addr, inet_ntoa), and manipulation of socket addresses (struct
sockaddr_in).
sys/socket.h: This header provides definitions for socket-related functions and structures, including
socket creation (socket), binding (bind), connecting (connect), and communication (send, recv).
BUF_SIZE is defined as 1024, which likely represents the size of a buffer or array.
OPSZ is defined as 4, which may represent the size of an operation or data type.
error_handling: It's a function that takes a pointer to a character (char*) as an argument, typically used
for error handling.
calculate: It's a function that takes an integer (opnum), an array of integers (opnds[]), and a character
(operator) as arguments. This function probably performs some calculation based on the operator and
operands provided.
error_handling: This function prototype suggests that the function error_handling takes a string message
(char*) as an argument. This function is likely intended to handle errors by displaying an error message.
calculate: This function prototype indicates that the function calculate takes three arguments:
int main(int argc, char *argv[]): This is the main function of the program. It accepts command-line
arguments argc (argument count) and argv (argument vector).
int serv_sock, clnt_sock;: Declares two integer variables serv_sock and clnt_sock which will hold socket
descriptors for server and client connections, respectively.
char opinfo[BUF_SIZE];: Declares a character array opinfo to store information. BUF_SIZE is likely a
constant defined elsewhere in the code, representing the size of the buffer.
int result, opnd_cnt, i;: Declares integer variables result, opnd_cnt, and i.
struct sockaddr_in serv_adr, clnt_adr;: Declares structures serv_adr and clnt_adr of type sockaddr_in.
These structures represent socket addresses for the server and client respectively.
socklen_t clnt_adr_sz;: Declares a variable clnt_adr_sz of type socklen_t which represents the size of the
client address.
if (argc!=2) { ... }: This if statement checks if the number of command-line arguments is not equal to 2. If
so, it prints a usage message indicating how to use the program and exits with a status of 1.
serv_sock = socket(PF_INET, SOCK_STREAM, 0);: This line creates a socket with the specified parameters:
PF_INET: This specifies the protocol family, which in this case is IPv4.
SOCK_STREAM: This specifies the type of socket, which in this case is a stream socket, commonly used
for TCP connections.
0: This usually represents the protocol within the family. Here, it's left as 0, which typically means the
default protocol for the specified family.
if (serv_sock == -1) error_handling("socket() error");: This line checks if the socket creation was
successful. If serv_sock is equal to -1, it indicates that an error occurred during socket creation, and it
calls an error_handling function to handle the error.
memset(&serv_adr, 0, sizeof(serv_adr));: This line initializes the memory block of the serv_adr structure
to all zeros. serv_adr is a struct sockaddr_in which holds socket address information.
serv_adr.sin_family = AF_INET;: Sets the address family of serv_adr to AF_INET, indicating that it is an
IPv4 address.
serv_adr.sin_port = htons(atoi(argv[1]));: Sets the port number in serv_adr to the port number provided
as a command-line argument (argv[1]). It converts the port number from string to integer using atoi, and
then converts it to network byte order using htons.
if (listen(serv_sock, 5) == -1) error_handling("listen() error");: This line sets the socket serv_sock to listen
for incoming connections. The second argument 5 specifies the maximum length of the queue for
pending connections. If an error occurs during the listen operation, it calls error_handling function to
handle the error.
Server Receiving Data:
The server seems to be receiving data from a client socket in a loop until it has received enough data.
This loop is controlled by the condition while ((opnd_cnt * OPSZ + 1) > recv_len).
It reads data from the client socket using the read() function and stores it in the opinfo buffer starting
from the index recv_len. The amount of data read is stored in recv_cnt.
Calculation:
After receiving all the necessary data, it calls the calculate() function to perform the arithmetic operation
based on the received operands and operator.
The calculate() function takes three arguments: the number of operands (opnum), an array of integers
representing the operands (opnds), and the operator (op).
It initializes the result with the first operand (opnds[0]) and then iterates over the remaining operands
based on the given operator. It performs the specified operation (+, -, or *) accordingly.
Sending Result:
Once the calculation is done, the server sends the result back to the client using the write() function.
Closing Sockets:
Finally, it closes the client socket (clnt_sock) and then the server socket (serv_sock).
fputs(message, stderr);: This line writes the string pointed to by message to the standard error stream
(stderr). The fputs() function is typically used to write strings to a stream. stderr is commonly used for
error messages instead of the standard output stream (stdout).
fputc('\n', stderr);: This line writes a newline character ('\n') to the standard error stream (stderr). This
adds a newline after the error message for better readability.
exit(1);: This line terminates the program with an exit status of 1. The exit status indicates to the
calling environment whether the program terminated successfully or encountered an error.
Conventionally, a non-zero exit status (such as 1) is used to indicate an error condition.