基于UDP/TCP套接字编程的小型远程备份系统
项目简介:
用一个简单的协议在服务器和服务器之间交换信息。在这个备份系统中,您可以使用客户机列出已经存储在远程服务器的备份文件夹。您还可以发送一个本地文件,将其存储在服务器上的备份文件夹中。如果您不再需要某个文件,您可以让客户端发送命令来从服务器的文件中删除该文件备份文件夹。您也可以修改服务器备份文件夹中的文件名称等等。
该协议包括六个命令功能“ls”、“send”、“remove”、“rename”、“quit”和“shutdown”。启动服务器和客户端后,用户从标准输入(如键盘)发送输入命令以交互方式发送到客户端,然后客户端将接收的命令通过网络发送到服务器。之后,服务器将执行相应的功能并发回客户端请求的信息。
当客户机从用户那里收到“ls”命令时,它会查询服务器并列出所有服务器备份文件夹中的可用文件。
当客户端接收到“send”命令以及特定文件名,客户端将此文件名对应的文件发送到服务器,服务器将其存储在备份文件夹。
当客户端接收到“remove”命令以及特定文件名,客户端将此发送到服务器,服务器从备份文件夹中删除此文件名对应的文件。
客户端收到“rename”和“原始文件名*(服务器的备份文件夹)和用户的“预期文件名”,它将向服务器发送命令将文件从“原始文件名”更改为“预期文件名”。
客户端使用“shutdown”命令停止服务器,并使用“quit”命令停止客户端所在终端。
流程实现:
启动服务器程序后,服务器启动一个端口上的UDP套接字。端口号可以从命令行给出,如果没有从命令中给出,则由操作系统自动分配。然后服务器将打印端口号,并等待来自客户机的UDP命令端口。客户端程序启动后,客户端将打印“$”并等待来自标准输入(键盘),并通过服务器的UDP端口发送UDP命令消息到服务器。
开机后,每次进入等待状态,服务器都打印
“Waiting UDP command@:xxxxx”。(xxxxx为端口号)
如果服务器接收到一个命令,它将打印
“[CMD RECEIVED]: xxx”。其中xxx为键入Cmd_T)。
如果来自用户的命令都不是支持的命令,客户端将打印“-错误命令”
ls指令实现:
此指令列出服务器备份文件夹中可用的文件。它使用UDP套接字。下图显示了详细实现此功能的程序。在启动服务器和客户端程序之后,如果用户有一个“ls”命令输入,客户端将通过命令消息发送“cmd = CMD_ LS”命令到服务器。收到此消息后,服务器将检查备份文件夹并返回一个带有" cmd = CMD_ LS"和" size = N "的消息来通知客户端总数为(N)的文件。如果文件夹中没有文件或文件夹不存在,服务器将发送“size = 0”的信息,否则服务器将通过一个数据消息传输每个文件名到客户端,客户端将在接收时显示每个文件名。完成“ls”后
函数,服务器和客户端都将返回到可以接收下一个命令的初始状态从用户输入。
下面是需要打印到控制台的消息:
如果服务器发现备份文件夹为空,服务器将打印“-server backup folder is empty”
如果客户端收到服务器为空的消息,客户端将打印“- server backup folder is empty”
服务器和客户端应分别在单行中以“-”前缀打印出每个文件名
如果客户端收到一个“cmd”字段而不是“cmd_LS”的消息,它将打印“- command”反应错误。”
send指令实现:
该指令向服务器发送一个特定的文件,服务器将把它存储在它的备份文件夹中。它使用UDP和TCP套接字实现功能,如下图客户端接收用户命令“send myFilename”,客户端将发送UDP消息"cmd= CMD_SEND ", " filename=myFilename ", " size=filesize “和” error=0 “发送到服务器。请注意,任何正常的消息都应该有一个“error=0”。服务器在接到带有上述消息的命令后检查备份文件夹中是否存在同名文件。根据备份文件夹中文件的存在情况分别处理,如果文件已经存在,服务器将返回一个错误消息,其中包括” error=2 “和” cmd= CMD_SEND “,并打印“file xxx exist; overwrite?”(请注意,错误消息与正常消息具有相同的结构消息,但是是非零错误码)。一旦客户机接收到错误消息,客户机就会进行打印“file exists. overwrite? (y/n): “,并等待用户从键盘输入。如果不是” Y “或者“y”从用户接收,客户端发送一个“error=2”和“cmd= CMD_SEND”的错误消息到服务器。一旦服务器接收到此错误消息,服务器将返回到“Waiting”,客户端也是如此。如果用户输入是“Y”或“y”,客户端需要发送一条正常的消息” error=0 “和” cmd= CMD_SEND"到服务器。
如果备份文件夹中不存在该文件,或者正常提示“error=0”和“cmd= CMD_SEND”。服务器用接收到的文件名在备份文件夹中打开一个文件。然后,服务器将打开一个TCP连接,其端口号由操作系统分配,并将此TCP端口号发送回客户端。然后,服务器将在这里等待
从客户端连接的端口,用于接收文件数据。如果服务器将来无法打开文件,服务器将返回一个错误消息“cmd = CMD_SEND”,“port=0”和“error= 1”给客户端。在这种情况下,如果客户端接收到” error=1 ",客户端和服务器都将返回到“Waiting”状态。
客户端接收到TCP端口号不为零的消息后,发起与TCP端口上的服务器三次握手。如果建立了连接,客户端将原始文件划分为若干段,并将每个段发送到服务器,直到到达文件结束。当文件数据传输时完成后,客户端将等待来自服务器的确认消息。服务器将写入每一个接收段按接收顺序发送到本地文件。一旦服务器接收到整个文件,则服务器将关闭文件,并返回一个消息,其中包含“cmd= CMD_ACK”和“error=0”到客户端,并且客户端(在收到这个确认之后)和服务器都将关闭TCP连接,然后返回到“Waiting”状态,等待下一个用户命令。接收文件期间,服务器应打印每次接收后接收的字节总数。如果期间发生任何错误服务器将终止接收并返回确认消息“error= 1”。以下信息应打印到控制台
if the client receives “error=2”, it shall print “file exists. overwrite? (y/n):” and wait for user input
if the server receives “error=2”, it shall return to ”Waiting” state
if the client cannot open the file for sending, it shall print “ - cannot open file: xxx”. (xxx is filename),
otherwise it shall print “ - filesize:xxx”. (xxx is filesize in bytes)
if the server fails to open a file for writing, it shall print “ - open file xxx error.”, otherwise it prints “ -filename:xxx” and “ - filesize:xxx”
if TCP binding is listening on a port, the server shall print “ - listen @:xxx”. (xxx is TCP port number)
if server successfully accepts the connection request, it shall print “ - connected with the client.”
if any error happens during the message reception on the server, the server shall print “ - message
reception error.”
if the server sends the acknowledgement, it shall print “ - send acknowledgement”.
if client receives “port=0”, “cmd6=CMD SEND” or “error=1” from the server, the client shall
prints “ - error or incorrect response from server.”, otherwise it prints “ - TCP port:xxx”. (xxx is port
number)
if client receives an acknowledgement without error (error=0), it shall print “ - file transmission is
completed.”, otherwise it prints “ - file transmission is failed”
remove指令实现:
该指令从服务器的备份文件夹中删除一个文件。这个过程从服务器和客户机的等待状态开始。在等待状态期间,如果在客户端接收到来自用户输入的命令“remove myFilename”,客户端将发送一个带有" CMD_REMOVE “的命令消息和” filename=myFilename "发送到服务器。然后服务器将尝试从备份中删除该文件。如果文件存在,服务器将执行删除,并发送一个“cmd= CMD_ACK”和和“error = 0”的确认消息。如果文件不存在,服务器将返回带有的确认消息“error= 1”。下面的消息需要打印到控制台。
if the corresponding file is not in the backup folder, the server shall print “ - file doesn’t exist”
if the client receive with “error=1”, it shall print “ - file doesn’t exist.”
rename指令实现:
该指令从服务器的备份文件夹重命名一个文件。在等待状态下,如果客户端接收到用户输入命令“rename myFilename”时,客户端将发送一条带有" CMD_RENAME "的命令消息和“原文件名=myFilename1,预期文件名=myFilename2”到服务器。然后服务器将尝试从备份文件夹重命名该文件。如果文件存在,服务器将执行重命名并发送一个“cmd= CMD_ACK”和“error=0”的确认消息。如果文件不存在
存在,服务器将返回“error=1”的确认消息。以下消息需要打印到控制台
if the corresponding file is not in the backup folder, the server shall print “ - file doesn’t exist”
if the client receive with “error=1”, it shall print “ - file doesn’t exist.”
quit和shutdown指令实现:
quit命令设计用于客户端退出执行。一旦客户端收到这个命令,它将退出程序。退出前不需要与服务器通信。然而shutdown命令被设计为从客户机发送到服务器,以关闭服务器。如果服务器接收到一个关机命令,它会返回一个“error=0”的确认信息,然后退出程序。客户端在收到此确认后将转向等待状态。
具体实现:
协议消息类型定义
我们定义了两种用于实现协议的消息类型:命令消息(Cmd_Msg_T)和数据消息(Data_Msg_T)。命令消息用于发送命令并交换必要的信息,以协助功能。例如,当您实现“ls”功能时,Cmd_Msg_T作为命令消息被发送,文件的数量也作为命令消息发送回客户端,以协助将来的文件名接收。但是,准确的文件名作为数据消息Data_Msg_T发送。
Cmd_Msg_T
该命令消息包含以下三个字段。为了支持不同计算机架构之间的通信,我们使用了定义在stdint.h库中的数据类型。消息数据类型在message.h中定义。命令消息(Cmd_Msg_T)包含以下元素:
command type: 8 bits unsigned integer (uint8 t)
filename: character [FILE NAME LEN]
size: 32 bits unsigned integer (uint32 t)
port: 16 bits unsigned integer (uint16 t)
error: 16 bits unsigned integer (uint16 t)
具体实现:
typedef struct CMD_MSG_tag
{
uint8_t cmd;
char filename[FILE_NAME_LEN];
uint32_t size;
uint16_t port;
uint16_t error;
}Cmd_Msg_T;
Data_Msg_T
data: character [DATA_BUF_LEN]
具体实现:
typedef struct DATA_MSG_tag
{
char data[DATA_BUF_LEN]; // filename
}Data_Msg_T;
定义FILE NAME LEN为128,DATA BUF LEN为3000。这里我们注意到大小和端口是多字节整数。如果发送方和接收方具有不同的结构,使用不同的字节顺序来表示整数,那么直接通过网络传输它们会遇到一些问题。这个潜在的问题可以通过使用htons()和htonl()来解决,将大小和端口从主机字节顺序转换为发送端用于传输的网络字节顺序。因此,接收方可以使用ntohs()和ntohl()将接收到的数据转换回主机字节顺序。
CMD_T指令类型
typedef enum CMD_tag
{
CMD_LS = 1,
CMD_SEND = 2,
CMD_GET = 3,
CMD_REMOVE = 4,
CMD_RENAME = 5,
CMD_SHUTDOWN = 6,
CMD_QUIT = 7,
CMD_ACK = 8,
}Cmd_T;
message.h文件
/*
* message.h
*/
#ifndef _MESSAGE_H_
#define _MESSAGE_H_
#define DATA_BUF_LEN 3000
#define FILE_NAME_LEN 128
typedef struct DATA_MSG_tag
{
char data[DATA_BUF_LEN]; // filename
}Data_Msg_T;
typedef enum CMD_tag
{
CMD_LS = 1,
CMD_SEND = 2,
CMD_GET = 3,
CMD_REMOVE = 4,
CMD_RENAME = 5,
CMD_SHUTDOWN = 6,
CMD_QUIT = 7,
CMD_ACK = 8,
}Cmd_T;
typedef struct CMD_MSG_tag
{
uint8_t cmd;
char filename[FILE_NAME_LEN];
uint32_t size;
uint16_t port;
uint16_t error;
}Cmd_Msg_T;
#endif
客户端实现:
client.h
#ifndef _CLIENT_H
#define _CLIENT_H
#include <sys/types.h>
#include<string>
#include "message.h"
using namespace std;
typedef enum CLIENT_STATE_tag
{
WAITING = 0,
PROCESS_LS = 1,
PROCESS_SEND = 2,
PROCESS_GET = 3,
PROCESS_REMOVE = 4,
PROCESS_RENAME = 5,
SHUTDOWN = 6,
QUIT = 7
}Client_State_T;
Cmd_Msg_T * create_cmd_msg();
Cmd_Msg_T * create_cmd_msg(uint8_t m_cmd, string file_name, uint32_t m_size, uint16_t m_port, uint16_t m_error);
Data_Msg_T * create_data_mag();
char * read_file(string s,int &size);
#endif
client.c
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <vector>
#include <string.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <unistd.h>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sstream>
#include <string>
#include <netdb.h>
#include <algorithm>
#include <unistd.h>
#include <fcntl.h>
#include "message.h"
#include "client.h"
using namespace std;
void ceate_tcp(int &sk,int port_out){
sockaddr_in remote ; // socket address for remote side
// create the socket
sk = socket(AF_INET,SOCK_STREAM,0) ;
if(sk < 0) {
cout<<" - failed to create TCP socket"<<endl;
exit(-1);
}
// designate the addressing family
remote.sin_family = AF_INET ;
// get the address of the remote host and store
remote.sin_addr.s_addr = inet_addr("127.0.0.1");
// get the port used on the remote side and store
remote.sin_port = port_out;
// connect to other side
if(connect(sk, (struct sockaddr *)&remote, sizeof(remote)) < 0) {
cout << " - failed to connect server with TCP"<<endl ;
close(sk);
exit(1);
}
}
int main(int argc, char *argv[])
{
unsigned short udp_port = 0;
const char* server_host = "127.0.0.1";
//process input arguments
if ((argc != 3) && (argc != 5))
{
cout << "Usage: " << argv[0];
cout << " [-address <server_host>] -port <udp_port>" << endl;
return 1;
}
else
{
//system("clear");
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-port") == 0)
udp_port = (unsigned short) atoi(argv[++i]);
else if (strcmp(argv[i], "-address") == 0)
{
server_host = argv[++i];
if (argc == 3)
{
cout << "Usage: " << argv[0];
cout << " [-address <server_host>] -port <udp_port>" << endl;
return 1;
}
}
else
{
cout << "Usage: " << argv[0];
cout << " [-address <server_host>] -port <udp_port>" << endl;
return 1;
}
}
}
int sockfd_udp;
hostent *hp ; // address of remote host
sockaddr_in remote ; // socket address for remote
sockaddr_in local ; // socket address for us
socklen_t rlen = sizeof(remote) ; // length of remote address
socklen_t len = sizeof(local) ; // length of local address
int moredata = 1 ; // keep processing or quit
int mesglen ; // actual length of message
//create udp socekt
sockfd_udp = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd_udp < 0) {
cout<<" - failed to create UDP socket"<<endl;
exit(-1);
}
// init data is zero
memset(&local, 0, len);
// TCP/IP – IPv4
local.sin_family = AF_INET;
// bind port
local.sin_port = 0; //let system choose the port
local.sin_addr.s_addr = htonl(INADDR_ANY);
// bind the name (address) to a port
if(::bind(sockfd_udp, (struct sockaddr*)& local, sizeof(local)) == -1){
cout<<" - failed to bind UDP socket"<<endl;
exit(1);
}
// init data is zero
memset(&remote, 0, rlen);
// TCP/IP – IPv4
remote.sin_family = AF_INET;
// bind port
remote.sin_port = udp_port; //let system choose the port
// get the address of the remote host and store
hp = gethostbyname(server_host) ;
memcpy(&remote.sin_addr,hp->h_addr,hp->h_length) ;
Client_State_T client_state = WAITING;
string in_cmd;
string file_name1, file_name2;
char buf[DATA_BUF_LEN] ; // buffer from remote
while(true)
{
usleep(100);
switch(client_state)
{
case WAITING:
{
cout<<"$ ";
cin>>in_cmd;
if(in_cmd == "ls")
{
client_state = PROCESS_LS;
}
else if(in_cmd == "send")
{
cin>>file_name1;
client_state = PROCESS_SEND;
}
else if(in_cmd == "remove")
{
cin>>file_name1;
client_state = PROCESS_REMOVE;
}
else if(in_cmd == "rename")
{
cin>>file_name1>>file_name2;
client_state = PROCESS_RENAME;
}
else if(in_cmd == "shutdown")
{
client_state = SHUTDOWN;
}
else if(in_cmd == "quit")
{
client_state = QUIT;
}
else
{
cout<<" - wrong command."<<endl;
client_state = WAITING;
}
break;
}
case PROCESS_LS:
{
Cmd_Msg_T *cmd_send_info = create_cmd_msg();
cmd_send_info->cmd = '1';
sendto(sockfd_udp,cmd_send_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_send_info);
memset(buf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,buf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Cmd_Msg_T *cmd_receive_info = (Cmd_Msg_T* )buf;
int cmd_flag = int(cmd_receive_info->cmd)-48;
if(cmd_flag == 1){
if(!cmd_receive_info->size){
cout<<" - server backup folder is empty"<<endl;
}else{
while(cmd_receive_info->size--){
char rebuf[DATA_BUF_LEN];
memset(rebuf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,rebuf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Data_Msg_T *data_info = (Data_Msg_T *)rebuf;
cout<<" "<<"- "<<data_info->data<<endl;
}
}
}else{
cout<<" - command response error"<<endl;
}
client_state = WAITING;
break;
}
case PROCESS_SEND:
{
int fd = open(file_name1.c_str(), O_RDWR);
if(fd == -1){
cout<<" - cannot open file: "<<file_name1<<endl;
client_state = WAITING;
break;
}
close(fd);
int file_size;
char * data = read_file(file_name1,file_size);
Cmd_Msg_T *cmd_send_info = create_cmd_msg('2', file_name1, file_size, 0, 0);
sendto(sockfd_udp,cmd_send_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_send_info);
char rebuf[DATA_BUF_LEN];
memset(rebuf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,rebuf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Cmd_Msg_T *cmd_re_info = (Cmd_Msg_T *)rebuf;
int port_num;
if(cmd_re_info->error==2){
cout<<"file exists. overwrite? (y/n): "<<endl;
char user_cmd;
cin>>user_cmd;
if(user_cmd == 'Y' || user_cmd == 'y'){
Cmd_Msg_T *cmd_send_info = create_cmd_msg('2', "", 0, 0, 0);
sendto(sockfd_udp,cmd_send_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_send_info);
cout<<"daad1:"<<port_num<<endl;
char rebuf[DATA_BUF_LEN];
memset(rebuf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,rebuf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Cmd_Msg_T *info1 = (Cmd_Msg_T *)rebuf;
if(info1->error==1 || info1->port==0 || info1->cmd != '2'){
cout<<" - error or incorrect response from server."<<endl;
client_state = WAITING;
break;
}else{
port_num = info1->port;
}
}else{
Cmd_Msg_T *cmd_send_info = create_cmd_msg('2', "", 0, 0, 2);
sendto(sockfd_udp,cmd_send_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_send_info);
client_state = WAITING;
break;
}
}else{
if(cmd_re_info->error==1 || cmd_re_info->port==0 || cmd_re_info->cmd != '2'){
cout<<" - error or incorrect response from server."<<endl;
client_state = WAITING;
break;
}else{
port_num = cmd_re_info->port;
}
}
cout<<" - filesize: "<< file_size<<endl;
cout<<" - TCP port: "<< port_num<<endl;
//建立tcp连接
int sk;
ceate_tcp(sk,port_num);
//准备发送数据
int error_flag = 0;
int segement = file_size;
ssize_t totallen = 0;
ssize_t writeLen = 0;
int count = 0;
while(segement){
usleep(10);
++count;
if(segement >= DATA_BUF_LEN ){
writeLen = write(sk, data+totallen, DATA_BUF_LEN);
}else{
writeLen = write(sk, data+totallen, segement);
}
if (writeLen < 0) {
error_flag = 1;
break;
}
else
{
totallen+=writeLen;
segement-=writeLen;
cout<<"Buffer size: "<< writeLen<<endl;
}
}
if(error_flag){
client_state = WAITING;
break;
}else{
cout<<"Buffer size: "<< 0 <<endl;
cout<<"Total Segment Number is: "<<count<<endl;
}
close(sk);
memset(rebuf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,rebuf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Cmd_Msg_T *ack_info = (Cmd_Msg_T *)rebuf;
if(ack_info->cmd == '8' && ack_info->error==0){
cout<<" - file transmission is completed."<<endl;
}else{
cout<<" - file transmission is failed."<<endl;
}
client_state = WAITING;
break;
}
case PROCESS_REMOVE:
{
Cmd_Msg_T *cmd_send_info = create_cmd_msg('4', file_name1, 0, 0, 0);
sendto(sockfd_udp,cmd_send_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_send_info);
char rebuf[DATA_BUF_LEN];
memset(rebuf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,rebuf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Cmd_Msg_T *cmd_re_info = (Cmd_Msg_T *)rebuf;
if(cmd_re_info->cmd == '8' && cmd_re_info->error == 0){
cout<< " - file is removed"<<endl;
}else{
cout<<" - file doesn't exist." <<endl;
}
client_state = WAITING;
break;
}
case PROCESS_RENAME:
{
Cmd_Msg_T *cmd_send_info = create_cmd_msg('5', file_name1+" "+file_name2, 0, 0, 0);
sendto(sockfd_udp,cmd_send_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_send_info);
char rebuf[DATA_BUF_LEN];
memset(rebuf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,rebuf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Cmd_Msg_T *cmd_re_info = (Cmd_Msg_T *)rebuf;
if(cmd_re_info->cmd == '8' && cmd_re_info->error == 0){
cout<< " - file has been renamed"<<endl;
}else{
cout<<" - file doesn't exist." <<endl;
}
client_state = WAITING;
break;
}
case SHUTDOWN:
{
Cmd_Msg_T *cmd_send_info = create_cmd_msg('6', "", 0, 0, 0);
sendto(sockfd_udp,cmd_send_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_send_info);
char rebuf[DATA_BUF_LEN];
memset(rebuf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,rebuf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Cmd_Msg_T *cmd_re_info = (Cmd_Msg_T *)rebuf;
if(cmd_re_info->cmd == '8' && cmd_re_info->error == 0){
cout<< " - server is shutdown"<<endl;
}else{
cout<<" - task is doning." <<endl;
}
client_state = WAITING;
break;
}
case QUIT:
{
exit(0);
}
default:
{
client_state = WAITING;
break;
}
}
}
return 0;
}
Cmd_Msg_T * create_cmd_msg(uint8_t m_cmd, string file_name, uint32_t m_size, uint16_t m_port, uint16_t m_error){
Cmd_Msg_T *cmd_info = (Cmd_Msg_T* )malloc(sizeof(Cmd_Msg_T));
memset(cmd_info, 0, sizeof(Cmd_Msg_T));
cmd_info->cmd = m_cmd;
memcpy(cmd_info->filename,file_name.c_str(),file_name.size());
cmd_info->size = m_size;
cmd_info->port = m_port;
cmd_info->error = m_error;
return cmd_info;
}
Cmd_Msg_T * create_cmd_msg(){
Cmd_Msg_T *cmd_info = (Cmd_Msg_T* )malloc(sizeof(Cmd_Msg_T));
memset(cmd_info, 0, sizeof(Cmd_Msg_T));
return cmd_info;
}
Data_Msg_T * create_data_mag(){
Data_Msg_T *data_info = (Data_Msg_T* )malloc(sizeof(Data_Msg_T));
memset(data_info, 0, sizeof(Data_Msg_T));
return data_info;
}
char * read_file(string s,int &size){
ifstream infile;
infile.open(s.c_str(),ios::in|ios::binary);
infile.seekg(0, ios::end);
int length=infile.tellg();
infile.seekg(0, ios::beg);
size = length;
char *data = (char *)malloc(length);
infile.read(data,length);
infile.close();
return data;
}
服务器端实现:
server.h
#ifndef _SERVER_H
#define _SERVER_H
#include <vector>
#include <string>
#include "message.h"
using namespace std;
typedef enum SERVER_STATE_tag
{
WAITING = 0,
PROCESS_LS = 1,
PROCESS_SEND = 2,
PROCESS_GET = 3,
PROCESS_REMOVE = 4,
PROCESS_RENAME = 5,
SHUTDOWN = 6
}Server_State_T;
bool checkFile(const char *fileName);
int checkDirectory (string dir);
int getDirectory (string dir, vector<string> &files);
Cmd_Msg_T * create_cmd_msg();
Cmd_Msg_T * create_cmd_msg(uint8_t m_cmd, string file_name, uint32_t m_size, uint16_t m_port, uint16_t m_error);
Data_Msg_T * create_data_mag();
#endif
Server.c
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <vector>
#include <string.h>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <unistd.h>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sstream>
#include <string>
#include <unistd.h>
#include <fcntl.h>
#include "message.h"
#include "server.h"
using namespace std;
string dir_name = "backup";
Server_State_T server_state;
string cmd_string[] = {" ", "CMD_LS", "CMD_SEND","CMD_GET","CMD_REMOVE","CMD_RENAME","CMD_SHUTDOWN"};
void tcp_tr(int &sk, int &sk2, __uint16_t & tcp_port_num){
sockaddr_in remote ; // socket address for remote
sockaddr_in local ; // socket address for us
char buf[DATA_BUF_LEN] ; // buffer from remote
char retbuf[DATA_BUF_LEN] ; // buffer to remote
socklen_t rlen = sizeof(remote) ; // length of remote address
socklen_t len = sizeof(local) ; // length of local address
int moredata = 1 ; // keep processing or quit
int mesglen ; // actual length of message
// create the socket
sk = socket(AF_INET,SOCK_STREAM,0);
if(sk < 0) {
cout<<" - failed to create TCP socket"<<endl;
exit(-1);
}
memset(&local, 0, len);
// set up the socket
local.sin_family = AF_INET ; // internet family
local.sin_port = 0;
local.sin_addr.s_addr = htonl(INADDR_ANY) ; // wild card machine address
// let system choose the port
// bind the name (address) to a port
if(::bind(sk,(struct sockaddr *)&local,sizeof(local)) == -1){
cout<<" - failed to bind TCP socket"<<endl;
exit(-1);
}
// get the port name and print it out
getsockname(sk,(struct sockaddr *)&local,&len) ;
tcp_port_num = local.sin_port;
// tell OS to queue (up to 1) connection requests
listen(sk, 1);
cout<<" - listen @: "<< tcp_port_num<<endl;
}
int main(int argc, char *argv[])
{
unsigned short udp_port = 0;
if ((argc != 1) && (argc != 3))
{
cout << "Usage: " << argv[0];
cout << " [-port <udp_port>]" << endl;
return 1;
}
else
{
//system("clear");
//process input arguments
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-port") == 0)
udp_port = (unsigned short) atoi(argv[++i]);
else
{
cout << "Usage: " << argv[0];
cout << " [-port <udp_port>]" << endl;
return 1;
}
}
}
int sockfd_udp;
char buf[DATA_BUF_LEN]; // buffer from remote
sockaddr_in remote ; // socket address for remote
sockaddr_in local ; // socket address for us
socklen_t rlen = sizeof(remote) ; // length of remote address
socklen_t len = sizeof(local) ; // length of local address
int moredata = 1 ; // keep processing or quit
int mesglen ; // actual length of message
//create udp socekt
sockfd_udp = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd_udp < 0) {
cout<<" - failed to create UDP socket"<<endl;
exit(-1);
}
// init data is zero
memset(&local, 0, len);
// TCP/IP – IPv4
local.sin_family = AF_INET;
// bind port
local.sin_port = udp_port; //let system choose the port
local.sin_addr.s_addr = htonl(INADDR_ANY);
// bind the name (address) to a port
if(::bind(sockfd_udp, (struct sockaddr*)& local, sizeof(local)) == -1){
cout<<" - failed to bind UDP socket"<<endl;
exit(1);
}
// get the port name and print it out
getsockname(sockfd_udp,(struct sockaddr *)&local,&len) ;
Cmd_Msg_T *cmd_info;
while(true)
{
usleep(100);
switch(server_state)
{
case WAITING:
{
cout << "**************************************************************" <<endl;
cout << "This is the server of IERG3310 Lab2 from : 1155100069" <<endl;
cout << "**************************************************************" <<endl;
cout << "Waiting UDP command @: " << local.sin_port <<endl;
break;
}
case PROCESS_LS:
{
vector<string> all_file;
int ret = getDirectory(dir_name, all_file);
if(ret || all_file.size()==0){
cout<<" - server backup folder is empty"<<endl;
}
Cmd_Msg_T *cmd_se_info = create_cmd_msg('1',"",all_file.size(),0, 0);
sendto(sockfd_udp,cmd_se_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
if(cmd_se_info->cmd == '1'&& cmd_se_info->size!=0){
for(auto it : all_file){
usleep(2);
Data_Msg_T* data_info = create_data_mag();
memcpy(data_info->data,&it,it.size());
sendto(sockfd_udp,data_info,sizeof(Data_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(data_info);
cout<<" "<<"- "<<it <<endl;
}
}
free(cmd_se_info);
server_state = WAITING;
break;
}
case PROCESS_SEND:
{
int sk,sk2;
uint16_t port_num;
char a[FILE_NAME_LEN];
memset(a, 0, FILE_NAME_LEN);
string root = "./backup/";
memcpy(a,root.c_str(),root.size());
memcpy(a+root.size(),cmd_info->filename,strlen(cmd_info->filename));
if(checkFile(a)){
cout<< "file "<<cmd_info->filename <<" exist; overwrite?"<<endl;
Cmd_Msg_T *cmd_se_info = create_cmd_msg('2',"",0 , 0, 2);
sendto(sockfd_udp,cmd_se_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_se_info);
char rebuf[DATA_BUF_LEN];
memset(rebuf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,rebuf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
Cmd_Msg_T *info_is_y = (Cmd_Msg_T *)rebuf;
if(info_is_y->error==2){
server_state = WAITING;
break;
}else{
tcp_tr(sk, sk2, port_num);
cout<<"daad2:"<<port_num<<endl;
Cmd_Msg_T *cmd_se_info = create_cmd_msg('2',"",0 ,port_num, 0);
sendto(sockfd_udp,cmd_se_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_se_info);
}
}else{
tcp_tr(sk, sk2, port_num);
Cmd_Msg_T *cmd_se_info = create_cmd_msg('2',"",0 ,port_num, 0);
sendto(sockfd_udp,cmd_se_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_se_info);
}
cout<<" - filename: "<< cmd_info->filename<<endl;
cout<<" - filesize: "<< cmd_info->size<<endl;
// 删除旧文件,创建新文件
// remove(a);
int fd = open(a, O_RDWR | O_CREAT | O_TRUNC);
if(fd == -1){
Cmd_Msg_T *cmd_se_info = create_cmd_msg('2',"",0 ,0, 1);
sendto(sockfd_udp,cmd_se_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_se_info);
cout<< " - open file "<<a<<" error."<<endl;
server_state = WAITING;
break;
}
usleep(5);
// wait for connection request, then close old socket
sk2 = accept(sk, (struct sockaddr *)0, (socklen_t *)0) ;
close(sk);
if(sk2 == -1){
cout << " - failed to accept TCP connection"<<endl;
exit(-1);
}
cout<<" - connected with client."<<endl;
char data[cmd_info->size];
memset(data, 0, cmd_info->size);
ssize_t totalLen = 0;
int error_flag = 0;
while (1)
{
usleep(5);
ssize_t readLen = read(sk2, data+totalLen, DATA_BUF_LEN);
if (readLen < 0) {
cout<< " - message reception error."<<endl;
error_flag = 1;
break;
}
else if (readLen == 0) {
break;
}
else
{
totalLen += readLen;
cout<<readLen<<endl;
cout<<" - total bytes received: "<< totalLen <<endl;
}
}
if(error_flag==0){
cout<<cmd_info->filename<<" has been received."<<endl;
write(fd,data, cmd_info->size);
// ofstream outfile;
// outfile.open(a,ios::out|ios::binary);
// outfile << data <<endl;
// outfile.close();
}
close(sk2);
Cmd_Msg_T *cmd_ack_info = create_cmd_msg('8',"",0 ,port_num, error_flag);
sendto(sockfd_udp,cmd_ack_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_ack_info);
cout<< " - send acknowledgement"<<endl;
server_state = WAITING;
break;
}
case PROCESS_REMOVE:
{
int error_flag;
char a[FILE_NAME_LEN];
memset(a, 0, FILE_NAME_LEN);
string root = "./backup/";
memcpy(a,root.c_str(),root.size());
memcpy(a+root.size(),cmd_info->filename,strlen(cmd_info->filename));
error_flag = remove(a);
if(error_flag == -1){
error_flag = 1;
cout<<" - file doesn't exist"<<endl;
}else{
cout<< " - "<<a<<" has been removed."<<endl;
}
Cmd_Msg_T *cmd_ack_info = create_cmd_msg('8', "", 0 , 0, error_flag);
sendto(sockfd_udp,cmd_ack_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_ack_info);
cout<< " - send acknowledgement"<<endl;
server_state = WAITING;
break;
}
case PROCESS_RENAME:
{ int nlen = strlen(cmd_info->filename);
int spacepos;
for(int i = 0; i < nlen; i++){
if(cmd_info->filename[i] == ' '){
spacepos = i;
}
}
string root = "./backup/";
char filename1[FILE_NAME_LEN];
char filename2[FILE_NAME_LEN];
memset(filename1, 0, FILE_NAME_LEN);
memcpy(filename1,root.c_str(),root.size());
memcpy(filename1+root.size(),cmd_info->filename,spacepos);
memset(filename2, 0, FILE_NAME_LEN);
memcpy(filename2,root.c_str(),root.size());
memcpy(filename2+root.size(),cmd_info->filename+spacepos + 1,nlen-spacepos);
char name[FILE_NAME_LEN];
memset(name, 0, FILE_NAME_LEN);
memcpy(name,cmd_info->filename+spacepos + 1,nlen-spacepos);
int error_flag;
if(checkFile(filename1)){
rename(filename1, filename2);
cout<<" - the file has been renamed to "<< name <<endl;
error_flag = 0;
}else{
cout<<" - file doesn't exist"<<endl;
error_flag = 1;
}
Cmd_Msg_T *cmd_ack_info = create_cmd_msg('8', "", 0 , 0, error_flag);
sendto(sockfd_udp,cmd_ack_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_ack_info);
cout<< " - send acknowledgement"<<endl;
server_state = WAITING;
break;
}
case SHUTDOWN:
{
int error_flag = 0;
Cmd_Msg_T *cmd_ack_info = create_cmd_msg('8', "", 0 , 0, error_flag);
sendto(sockfd_udp,cmd_ack_info,sizeof(Cmd_Msg_T),0,(struct sockaddr *)&remote, sizeof(remote));
free(cmd_ack_info);
cout<< " - send acknowledgement"<<endl;
exit(0);
}
default:
{
server_state = WAITING;
break;
}
}
memset(buf,0,DATA_BUF_LEN);
recvfrom(sockfd_udp,buf,DATA_BUF_LEN,0,(struct sockaddr *)&remote, &rlen);
//此处需要解析,并且打印指令数据,然后更新server_state,进而执行相应的操作
cmd_info = (Cmd_Msg_T* )buf;
int cmd_flag = int(cmd_info->cmd)-48;
cout<<"[CMD RECEIVED]: "<<cmd_string[cmd_flag]<<endl;
server_state = (Server_State_T)cmd_flag;
}
close(sockfd_udp);
return 0;
}
//this function check if the backup folder exist
int checkDirectory (string dir)
{
DIR *dp;
if((dp = opendir(dir.c_str())) == NULL) {
//cout << " - error(" << errno << ") opening " << dir << endl;
if(mkdir(dir.c_str(), S_IRWXU) == 0)
cout<< " - Note: Folder "<<dir<<" does not exist. Created."<<endl;
else
cout<< " - Note: Folder "<<dir<<" does not exist. Cannot created."<<endl;
return errno;
}
closedir(dp);
return 1;
}
//this function is used to get all the filenames from the
//backup directory
int getDirectory (string dir, vector<string> &files)
{
DIR *dp;
struct dirent *dirp;
if((dp = opendir(dir.c_str())) == NULL) {
//cout << " - error(" << errno << ") opening " << dir << endl;
if(mkdir(dir.c_str(), S_IRWXU) == 0)
cout<< " - Note: Folder "<<dir<<" does not exist. Created."<<endl;
else
cout<< " - Note: Folder "<<dir<<" does not exist. Cannot created."<<endl;
return errno;
}
int j=0;
while ((dirp = readdir(dp)) != NULL) {
//do not list the file "." and ".."
if((string(dirp->d_name)!=".") && (string(dirp->d_name)!=".."))
files.push_back(string(dirp->d_name));
}
closedir(dp);
return 0;
}
//this function check if the file exists
bool checkFile(const char *fileName)
{
ifstream infile(fileName);
return infile.good();
}
Cmd_Msg_T * create_cmd_msg(){
Cmd_Msg_T *cmd_info = (Cmd_Msg_T* )malloc(sizeof(Cmd_Msg_T));
memset(cmd_info, 0, sizeof(Cmd_Msg_T));
return cmd_info;
}
Data_Msg_T * create_data_mag(){
Data_Msg_T *data_info = (Data_Msg_T* )malloc(sizeof(Data_Msg_T));
memset(data_info, 0, sizeof(Data_Msg_T));
return data_info;
}
Cmd_Msg_T * create_cmd_msg(uint8_t m_cmd, string file_name, uint32_t m_size, uint16_t m_port, uint16_t m_error){
Cmd_Msg_T *cmd_info = (Cmd_Msg_T* )malloc(sizeof(Cmd_Msg_T));
memset(cmd_info, 0, sizeof(Cmd_Msg_T));
cmd_info->cmd = m_cmd;
memcpy(cmd_info->filename,file_name.c_str(),file_name.size());
cmd_info->size = m_size;
cmd_info->port = m_port;
cmd_info->error = m_error;
return cmd_info;
}
Makefile文件编写:
all:server client
server: server.cc message.h
g++ server.cc -g -o server
client: client.cc message.h
g++ client.cc -g -o client
clean:
rm -rf server client