网络通信三——1-13

// my_ftp_server.h
// 声明文件服务器的相关函数和常量

// 配置文件服务器的根目录
#define SERVER_ROOT_DIR "/home/vicliu/my_ftp_root"

// 响应客户端的list命令
// conn_fd  通过指定的连接套接字传递数据
void put_list_data_to_client(int conn_fd);

// 响应客户端的get命令
// conn_fd  连接套接字
// file     客户端待下载的文件名
void put_file_data_to_client(int conn_fd, char *file);

// 响应客户端的put命令
// conn_fd  连接套接字
// file     客户端待上传的文件名
void get_file_data_from_client(int conn_fd, char *file);
// my_ftp_client.h
// 文件服务器客户端的函数声明

// 客户端的list执行函数
void get_list_data_from_server(int sock_fd);

// 客户端的get执行函数
void get_file_data_from_server(int sock_fd, char *file);

// 客户端的put执行函数
void put_file_data_to_server(int sock_fd, char *file);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include "my_header.h"
#include "my_ftp_client.h"

#define N 150

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("usage : %s <server_ip> <server_port>\n", argv[0]);
        return 1;
    }
    
    int sock_fd = 0;
    struct sockaddr_in serveraddr;
    char buf[N] = {'\0'};
    
    struct data_package pkg;
    memset(&pkg, 0, sizeof(pkg));

    // 1.socket
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 2.connect
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    connect(sock_fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    
    // 3.循环从键盘读入命令并构造数据结构体发送给服务器
    while(1)
    {
        // 3.1输入
        printf("my_ftp>");
        fgets(buf, N, stdin);
        buf[strlen(buf)-1] = '\0';  // 消除'\n'
        
        // 3.2构造结构体发送,并准备接收服务器发来的数据
        if(strncmp(buf, "list", 4) == 0)
        {
            pkg.cmd = CMD_LIST;
            send(sock_fd, &pkg, sizeof(pkg), 0);
            // 执行接收list数据
            get_list_data_from_server(sock_fd);
        }
        else if(strncmp(buf, "get ", 4) == 0)
        {
            pkg.cmd = CMD_GET;
            strcpy(pkg.filename, buf+4);
            send(sock_fd, &pkg, sizeof(pkg), 0);
            // 执行下载函数
            get_file_data_from_server(sock_fd, buf+4);
        }
        else if(strncmp(buf, "put ", 4) == 0)
        {
            pkg.cmd = CMD_PUT;
            strcpy(pkg.filename, buf+4);
            send(sock_fd, &pkg, sizeof(pkg), 0);
            // 执行上传函数
            put_file_data_to_server(sock_fd, buf+4);
        }
        else if(strncmp(buf, "quit", 4) == 0)
        {
            pkg.cmd = CMD_QUIT;
            send(sock_fd, &pkg, sizeof(pkg), 0);
            // 执行退出操作
            printf("client quit...\n");
        }
        else 
        {
        }
    }
    
    // 4.close
    close(sock_fd);   
    
    return 0;
}

// 客户端的list执行函数
void get_list_data_from_server(int sock_fd)
{
    struct data_package pkg;
       
    // 循环接收并打印,直到收到结束标志为止
    while(1)
    {
        memset(&pkg, 0, sizeof(pkg));
        
        recv(sock_fd, &pkg, sizeof(pkg), 0);
        
        if(pkg.cmd != CMD_LIST)
            continue;
            
        if(pkg.flag == DATA_END_FLAG)
            break;
            
        // 打印数据
        printf("%s\n", pkg.data);
    }
}

// 客户端的get执行函数
void get_file_data_from_server(int sock_fd, char *file)
{
    struct data_package pkg;
    int fd = 0;
    int n = 0;

    // 1.打开待下载文件,如果文件不存在则创建,如果文件存在,则将其数据截短为0
    fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    
    // 2.循环读取服务器发送来的数据,并写入文件
    while(1)
    {
        recv(sock_fd, &pkg, sizeof(pkg), 0);
        
        if(pkg.cmd != CMD_GET)
            continue;
        
        if(pkg.flag == DATA_END_FLAG)
            break;
        
        n = strlen(pkg.data);   // 实际数据长度
        write(fd, pkg.data, n);
    }
    
    // 3.关闭文件
    close(fd);
}

// 客户端的put执行函数
void put_file_data_to_server(int sock_fd, char *file)
{
    struct data_package pkg;
    int fd;

    // 1.判断待上传文件是否存在,如果不存在,则发送数据结束状态包
    if(access(file, F_OK) == -1)
    {
        memset(&pkg, 0, sizeof(pkg));
        pkg.cmd = CMD_PUT;
        pkg.flag = DATA_END_FLAG;
        
        send(sock_fd, &pkg, sizeof(pkg), 0);
        
        return;
    }
    
    // 2.打开文件
    fd = open(file, O_RDONLY);
    
    // 3.循环读取文件内容,发送给服务器
    memset(&pkg, 0, sizeof(pkg));
    pkg.cmd = CMD_PUT;
    pkg.flag = 0;
    while(read(fd, pkg.data, MAX_DATA_LEN-1) > 0)
    {
        send(sock_fd, &pkg, sizeof(pkg), 0);
        
        memset(pkg.data, 0, MAX_DATA_LEN);
    }   
    
    // 4.构建数据结束状态包发送给服务器
    memset(&pkg, 0, sizeof(pkg));
    pkg.cmd = CMD_PUT;
    pkg.flag = DATA_END_FLAG;
    send(sock_fd, &pkg, sizeof(pkg), 0);
    
    // 5.关闭文件
    close(fd);
}

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <dirent.h>
#include "my_header.h"
#include "my_ftp_server.h"

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("usage : %s <server_ip> <server_port>\n", argv[0]);
        return 1;
    }
    
    int sock_fd = 0;
    int conn_fd = 0;
    int n = 0;
    struct sockaddr_in serveraddr;
    struct sockaddr_in clientaddr;
    socklen_t len = 0;
    
    // 存储客户端和服务器的交互数据
    struct data_package pkg;
    
    memset(&pkg, 0, sizeof(pkg));

    // 1.socket
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    
    // 2.bind
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    bind(sock_fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    
    // 3.listen
    listen(sock_fd, 15);
    
    // 4.accept
    while(1)
    {
        printf("waiting for connetion...\n");
        len = sizeof(clientaddr);
        conn_fd = accept(sock_fd, (struct sockaddr *)&clientaddr, &len);
        
        printf("connection with %s : %d\n", 
            inet_ntoa(clientaddr.sin_addr),
            ntohs(clientaddr.sin_port));
        
        while(1)
        {
            // 循环读取客户端发送来的数据,分析并执行相应操作
            // 直到客户端退出为止
            n = recv(conn_fd, &pkg, sizeof(pkg), 0);
            switch(pkg.cmd)
            {
                case CMD_LIST:
                    // 执行list操作
                    put_list_data_to_client(conn_fd);
                    break;
                case CMD_GET:
                    // 执行get操作
                    put_file_data_to_client(conn_fd, pkg.filename);
                    break;
                case CMD_PUT:
                    // 执行put操作
                    get_file_data_from_client(conn_fd,  pkg.filename);
                    break;
                case CMD_QUIT:
                    printf("server quit...\n");
                    // 执行quit操作
                    break;
                default:
                    break;
            }
        }
        
        // 5.关闭连接套接字
        close(conn_fd);
    }
    
    // 6.close监听套接字
    close(sock_fd);
    
    return 0;
}


// 响应客户端的list命令
// conn_fd  通过指定的连接套接字传递数据
void put_list_data_to_client(int conn_fd)
{
    struct data_package pkg;

    // 1.打开文件服务器根目录
    DIR *p_dir = opendir(SERVER_ROOT_DIR);
    
    // 2.循环读取目录项信息,发送给客户端
    struct dirent *p = NULL;
    while((p = readdir(p_dir)) != NULL)
    {
        if(strncmp(p->d_name, ".", 1) == 0)
            continue;
    
        memset(&pkg, 0, sizeof(pkg));
        pkg.cmd = CMD_LIST;
        strcpy(pkg.data, p->d_name);
        pkg.flag = 0;
        
        send(conn_fd, &pkg, sizeof(pkg), 0);
    }
    
    // 2.1发送数据结束状态数据包
    memset(&pkg, 0, sizeof(pkg));
    pkg.cmd = CMD_LIST;
    pkg.flag = DATA_END_FLAG;       // 结束标志
    send(conn_fd, &pkg, sizeof(pkg), 0);
    
    // 3.关闭目录
    closedir(p_dir);
}

// 响应客户端的get命令
// conn_fd  连接套接字
// file     客户端待下载的文件名
void put_file_data_to_client(int conn_fd, char *file)
{
    struct data_package pkg;
    char path[MAX_FILE_NAME_LEN] = {'\0'};
    int fd = 0;
    
    // 构造待下载文件在服务器文件系统中的绝对路径
    sprintf(path, "%s/%s", SERVER_ROOT_DIR, file);

    // 1.判断待下载文件是否存在,如果不存在,直接发送数据结束状态包 
    if(access(path, F_OK) == -1)
    {
        memset(&pkg, 0, sizeof(pkg));
        pkg.cmd = CMD_GET;
        pkg.flag = DATA_END_FLAG;
        
        send(conn_fd, &pkg, sizeof(pkg), 0);
        
        return;
    }
    
    // 2.如果文件存在,打开文件
    fd = open(path, O_RDONLY);
    
    // 3.循环的读取文件内容,发送给客户端
    memset(&pkg, 0, sizeof(pkg));
    pkg.cmd = CMD_GET;
    pkg.flag = 0;
    while(read(fd, pkg.data, MAX_DATA_LEN-1) > 0)
    {
        send(conn_fd, &pkg, sizeof(pkg), 0);
        memset(pkg.data, 0, MAX_DATA_LEN);
    }
        
    // 4.构造数据结束状态包
    memset(&pkg, 0, sizeof(pkg));
    pkg.cmd = CMD_GET;
    pkg.flag = DATA_END_FLAG;
    send(conn_fd, &pkg, sizeof(pkg), 0);
    
    // 5.关闭文件
    close(fd);
}

// 响应客户端的put命令
// conn_fd  连接套接字
// file     客户端待上传的文件名
void get_file_data_from_client(int conn_fd, char *file)
{
    struct data_package pkg;
    int fd = 0;
    int n = 0;
    char path[MAX_FILE_NAME_LEN] = {'\0'};

    // 构建待上传文件在服务器文件系统中的绝对路径
    sprintf(path, "%s/%s", SERVER_ROOT_DIR, file);

    // 1.打开文件,如果文件不存在,则创建;如果文件存在,则将其长度截短为0
    fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    
    // 2.循环读取文件内容,写入文件
    while(1)
    {
        recv(conn_fd, &pkg, sizeof(pkg), 0);
        
        if(pkg.cmd != CMD_PUT)
            continue;
            
        if(pkg.flag == DATA_END_FLAG)
            break;
        
        n = strlen(pkg.data);
        write(fd, pkg.data, n);
    }
    
    // 3.关闭文件
    close(fd);
}

推荐阅读更多精彩内容