网络通信二——1-12

//注意数据包中多字节在传输过程中的字节序的问题 htons/htonl
//通过socket发送结构体空间时的字节对齐导致的大小不一致问题
//使用某种技术实现发送数据的长度可变化的结构体
//方法一
struct msg_node
{
unsigned short len ; //发送的数据长度
char *data ;        //数据部分,以'\0'作为结束符
};
//方法二
struct msg_node
{
unsigned short len ; //发送的数据长度
char data[0] ;      //数据部分,以'\0'作为结束符       占位,
};
malloc
strcpy
//方法三
struct msg_node
{
unsigned short len; //发送的数据长度
char data[1] ;      //数据部分,以'\0'作为结束符
};
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "my_header.h"
#include "my_ftp_server.h"

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("usage : %s  \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);
}
// udp echo 客户端
// 从键盘读入数据,发送给服务器,并接收客户端回弹的数据,打印

#include 
#include 
#include 
#include 
#include 
#include 

#define N 1024

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("usage : %s  \n", argv[0]);
        return 1;
    }

    int sock_fd;
    char buf[N] = {'\0'};
    struct sockaddr_in serveraddr;
    
    memset(&serveraddr, 0, sizeof(serveraddr));

    // 1.socket     SOCK_DGRAM
    if((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket failed");
        return 1;
    }
    
    // 构造服务器地址
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    
    while(1)
    {   
        printf(">>>>>");
        fgets(buf, N, stdin);
        buf[strlen(buf)-1] = '\0';
        
        // 2.sendto
        // ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
        // 参数:
        // 前四个参数与send函数一致
        // dest_addr    提供发送目的地址结构体的起始地址
        // addrlen      发送目的地址结构体的长度
        sendto(sock_fd, buf, N, 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
        
        if(strncmp(buf, ".exit", 5) == 0)
            break;
    
        memset(buf, 0, N);
    
        // 3.recvfrom
        // ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
        // 参数:
        // 前四个参数与recv函数一致
        // src_addr     用于提供获取发送该数据的地址信息结构体的起始地址
        // addrlen      用于提供地址信息结构体的长度,并接收实际地址信息结构体的长度
        recvfrom(sock_fd, buf, N, 0, NULL, NULL);
        
        printf("echo from server : %s\n", buf);
    }
    // 4.close

    return 0;
}
// udp echo 客户端
// 从键盘读入数据,发送给服务器,并接收客户端回弹的数据,打印

#include 
#include 
#include 
#include 
#include 
#include 

#define N 1024

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("usage : %s  \n", argv[0]);
        return 1;
    }

    int sock_fd;
    char buf[N] = {'\0'};
    struct sockaddr_in serveraddr;
    
    memset(&serveraddr, 0, sizeof(serveraddr));

    // 1.socket     SOCK_DGRAM
    if((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket failed");
        return 1;
    }
    
    // 构造服务器地址
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));
    
    while(1)
    {   
        printf(">>>>>");
        fgets(buf, N, stdin);
        buf[strlen(buf)-1] = '\0';
        
        // 2.sendto
        // ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
        // 参数:
        // 前四个参数与send函数一致
        // dest_addr    提供发送目的地址结构体的起始地址
        // addrlen      发送目的地址结构体的长度
        sendto(sock_fd, buf, N, 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
        
        if(strncmp(buf, ".exit", 5) == 0)
            break;
    
        memset(buf, 0, N);
    
        // 3.recvfrom
        // ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
        // 参数:
        // 前四个参数与recv函数一致
        // src_addr     用于提供获取发送该数据的地址信息结构体的起始地址
        // addrlen      用于提供地址信息结构体的长度,并接收实际地址信息结构体的长度
        recvfrom(sock_fd, buf, N, 0, NULL, NULL);
        
        printf("echo from server : %s\n", buf);
    }
    // 4.close

    return 0;
}

你可能感兴趣的:(网络通信二——1-12)