网络通信二——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 <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);
}
// udp echo 客户端
// 从键盘读入数据,发送给服务器,并接收客户端回弹的数据,打印

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define N 1024

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("usage : %s <server_ip> <server_port>\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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define N 1024

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("usage : %s <server_ip> <server_port>\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;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,743评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,296评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,285评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,485评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,581评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,821评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,960评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,719评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,186评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,516评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,650评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,936评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,757评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,991评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,370评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,527评论 2 349

推荐阅读更多精彩内容

  • 1. 前天晚上,习惯性的坐在沙发上看手机,没看几分钟,爱人从我身边走过很不满的说了句:“不拿手机会死呀!” 我抬头...
    梅子Mey阅读 591评论 6 4
  • 当一段音乐仅仅为了和声曲式上的正确而存在时,这段音乐晦涩难听。只是为了写点什么而写的后果是封闭、隔离,写出的东西失...
    LynneGong阅读 382评论 0 0
  • 首先我想请读者原谅我的主观色彩。因为如果没有我的存在,这篇书评便找不到着力点。令我稍稍放心的是,这本书也是由第一人...
    昱凝声阅读 1,397评论 0 3
  • 很久没用js了,最近因为要做个项目,要用到前端,于是重拾起来看看。本文应该适合于跟我一样,用过js但是又记得不是很...
    doubimonkey阅读 380评论 0 50
  • 文/如如我慧 虽然我不会拳脚,只是一个小小职员,但我还是参与到了这场惨烈的争夺之中,心里唏嘘不已。后面还有几...
    如如我绘阅读 179评论 3 0