Skip to content
Snippets Groups Projects
Commit 14e3c369 authored by Ziheng Chen's avatar Ziheng Chen
Browse files

Upload New File

parent 86e7bfe1
Branches
No related tags found
No related merge requests found
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <fcntl.h>
#include "dshlib.h"
#include "rshlib.h"
/*
* exec_remote_cmd_loop(server_ip, port)
* server_ip: a string in ip address format, indicating the servers IP
* address. Note 127.0.0.1 is the default meaning the server
* is running on the same machine as the client
*
* port: The port the server will use. Note the constant
* RDSH_DEF_PORT which is 1234 in rshlib.h. If you are using
* tux you may need to change this to your own default, or even
* better use the command line override -c implemented in dsh_cli.c
* For example ./dsh -c 10.50.241.18:5678 where 5678 is the new port
* number and the server address is 10.50.241.18
*
* This function basically implements the network version of
* exec_local_cmd_loop() from the last assignemnt. It will:
*
* 1. Allocate buffers for sending and receiving data over the
* network
* 2. Create a network connection to the server, getting an active
* socket by calling the start_client(server_ip, port) function.
* 2. Go into an infinite while(1) loop prompting the user for
* input commands.
*
* a. Accept a command from the user via fgets()
* b. Send that command to the server using send() - it should
* be a null terminated string
* c. Go into a loop and receive client requests. Note each
* receive might not be a C string so you need to print it
* out using:
* printf("%.*s", (int)bytes_received, rsp_buff);
* this version of printf() uses the "%.*s" flag that indicates
* that the rsp_buff might be a null terminated string, or
* it might not be, if its not, print exactly bytes_received
* bytes.
* d. In the recv() loop described above. Each time you receive
* data from the server, see if the last byte received is the
* EOF character. This indicates the server is done and you can
* send another command by going to the top of the loop. The
* best way to do this is as follows assuming you are receiving
* data into a buffer called recv_buff, and you received
* recv_bytes in the call to recv:
*
* recv_bytes = recv(sock, recv_buff, recv_buff_sz, 0)
*
* if recv_bytes:
* <negative_number>: communication error
* 0: Didn't receive anything, likely server down
* > 0: Got some data. Check if the last byte is EOF
* is_eof = (recv_buff[recv_bytes-1] == RDSH_EOF_CHAR) ? 1 : 0;
* if is_eof is true, this is the last part of the transmission
* from the server and you can break out of the recv() loop.
*
* returns:
* OK: The client executed all of its commands and is exiting
* either by the `exit` command that terminates the client
* or the `stop-server` command that terminates both the
* client and the server.
* ERR_MEMORY: If this function cannot allocate memory via
* malloc for the send and receive buffers
* ERR_RDSH_CLIENT: If the client cannot connect to the server.
* AKA the call to start_client() fails.
* ERR_RDSH_COMMUNICATION: If there is a communication error, AKA
* any failures from send() or recv().
*
* NOTE: Since there are several exit points and each exit point must
* call free() on the buffers allocated, close the socket, and
* return an appropriate error code. Its suggested you use the
* helper function client_cleanup() for these purposes. For example:
*
* return client_cleanup(cli_socket, request_buff, resp_buff, ERR_RDSH_COMMUNICATION);
* return client_cleanup(cli_socket, request_buff, resp_buff, OK);
*
* The above will return ERR_RDSH_COMMUNICATION and OK respectively to the main()
* function after cleaning things up. See the documentation for client_cleanup()
*
*/
int exec_remote_cmd_loop(char *address, int port)
{
int cli_socket;
char *cmd_buff, *rsp_buff;
ssize_t recv_size;
// Allocate buffers for command input and response storage
cmd_buff = (char *)malloc(SH_CMD_MAX);
rsp_buff = (char *)malloc(RDSH_COMM_BUFF_SZ);
if (!cmd_buff || !rsp_buff) {
fprintf(stderr, "Error: Memory allocation failed\n");
return client_cleanup(-1, cmd_buff, rsp_buff, ERR_MEMORY);
}
// Start the client (connect to the server)
cli_socket = start_client(address, port);
if (cli_socket < 0) {
return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_CLIENT);
}
// Main client loop
while (1) {
// Print prompt and read user input
printf("%s", SH_PROMPT);
if (fgets(cmd_buff, SH_CMD_MAX, stdin) == NULL) {
printf("\n");
break; // Exit loop on EOF (Ctrl+D)
}
// Remove trailing newline
cmd_buff[strcspn(cmd_buff, "\n")] = '\0';
// Ignore empty input
if (strlen(cmd_buff) == 0) continue;
// Send command to the server (including null terminator)
if (send(cli_socket, cmd_buff, strlen(cmd_buff) + 1, 0) < 0) {
perror("Error sending command to server");
return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_COMMUNICATION);
}
// Receive response from server
while ((recv_size = recv(cli_socket, rsp_buff, RDSH_COMM_BUFF_SZ, 0)) > 0) {
// Check if last byte is the EOF character
int is_last_chunk = (rsp_buff[recv_size - 1] == RDSH_EOF_CHAR) ? 1 : 0;
// Replace EOF character with null terminator
if (is_last_chunk) {
rsp_buff[recv_size - 1] = '\0';
}
// Print response (handles both raw data and null-terminated strings)
printf("%.*s", (int)recv_size, rsp_buff);
// If EOF character received, stop receiving
if (is_last_chunk) break;
}
if (recv_size < 0) {
perror("Error receiving response from server");
return client_cleanup(cli_socket, cmd_buff, rsp_buff, ERR_RDSH_COMMUNICATION);
}
}
// Cleanup before exiting
return client_cleanup(cli_socket, cmd_buff, rsp_buff, OK);
//return WARN_RDSH_NOT_IMPL;
}
/*
* start_client(server_ip, port)
* server_ip: a string in ip address format, indicating the servers IP
* address. Note 127.0.0.1 is the default meaning the server
* is running on the same machine as the client
*
* port: The port the server will use. Note the constant
* RDSH_DEF_PORT which is 1234 in rshlib.h. If you are using
* tux you may need to change this to your own default, or even
* better use the command line override -c implemented in dsh_cli.c
* For example ./dsh -c 10.50.241.18:5678 where 5678 is the new port
* number and the server address is 10.50.241.18
*
* This function basically runs the client by:
* 1. Creating the client socket via socket()
* 2. Calling connect()
* 3. Returning the client socket after connecting to the server
*
* returns:
* client_socket: The file descriptor fd of the client socket
* ERR_RDSH_CLIENT: If socket() or connect() fail
*
*/
int start_client(char *server_ip, int port) {
int client_socket;
struct sockaddr_in server_addr;
// Step 1: Create the client socket
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket < 0) {
perror("Error creating client socket");
return ERR_RDSH_CLIENT;
}
// Step 2: Set up the server address struct
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); // Convert port to network byte order
// Step 3: Convert and assign the server IP address
if (inet_pton(AF_INET, server_ip, &server_addr.sin_addr) <= 0) {
perror("Invalid server IP address");
close(client_socket);
return ERR_RDSH_CLIENT;
}
// Step 4: Connect to the server
if (connect(client_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Error connecting to the server");
close(client_socket);
return ERR_RDSH_CLIENT;
}
printf("Connected to server at %s:%d\n", server_ip, port);
return client_socket; // Return the connected socket descriptor
}
/*
* client_cleanup(int cli_socket, char *cmd_buff, char *rsp_buff, int rc)
* cli_socket: The client socket
* cmd_buff: The buffer that will hold commands to send to server
* rsp_buff: The buffer that will hld server responses
*
* This function does the following:
* 1. If cli_socket > 0 it calls close(cli_socket) to close the socket
* 2. It calls free() on cmd_buff and rsp_buff
* 3. It returns the value passed as rc
*
* Note this function is intended to be helper to manage exit conditions
* from the exec_remote_cmd_loop() function given there are several
* cleanup steps. We provide it to you fully implemented as a helper.
* You do not have to use it if you want to develop an alternative
* strategy for cleaning things up in your exec_remote_cmd_loop()
* implementation.
*
* returns:
* rc: This function just returns the value passed as the
* rc parameter back to the caller. This way the caller
* can just write return client_cleanup(...)
*
*/
int client_cleanup(int cli_socket, char *cmd_buff, char *rsp_buff, int rc){
//If a valid socket number close it.
if(cli_socket > 0){
close(cli_socket);
}
//Free up the buffers
free(cmd_buff);
free(rsp_buff);
//Echo the return value that was passed as a parameter
return rc;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment