Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
cs503zchen
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Ziheng Chen
cs503zchen
Commits
14e3c369
Commit
14e3c369
authored
3 months ago
by
Ziheng Chen
Browse files
Options
Downloads
Patches
Plain Diff
Upload New File
parent
86e7bfe1
Branches
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
w8/rsh_cli.c
+259
-0
259 additions, 0 deletions
w8/rsh_cli.c
with
259 additions
and
0 deletions
w8/rsh_cli.c
0 → 100644
+
259
−
0
View file @
14e3c369
#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
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment