Linux网络编程【13】(项目实战)

1,203 阅读10分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

1 实现客户端下载服务器所在目录文件

客户端发送要下载的文件名给服务器,服务器判断文件是否存在,将结果告知客户端如果文件存在,服务器读取文件内容并发送给客户端,客户端接收文件内容并写入指定的文件。

1.1 服务器代码

//TCP网络编程之服务器

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h> 
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

//使用不需要定义,__FILE__指示当前文件名__func__指示当前函数名__LINE__指示运行当前文件的行数
//exit(1)异常退出
#define N 128
#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

typedef struct{
    int n;  //文件内容的大小
    char text[N]; //文件内容
}MSG;

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(1);
    }

    int sockfd, acceptfd;
    struct sockaddr_in serveraddr, clientaddr;
    socklen_t addrlen = sizeof(serveraddr);
    char buf[N] = {0};
    ssize_t bytes;
    int fd;
    ssize_t num;
    MSG msg;

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //第二步:填充服务器网络信息结构体
    //inet_addr:将点分十进制ip地址转换为网络字节序的无符号4字节整数
    //atoi:将数字型字符串转换为整形数据
    //htons:将主机字节序转化为网络字节序
    serveraddr.sin_family = AF_INET;
    //注意:ip地址不能随便写,服务器在那个主机中运行,ip地址就是这个主机的
    //如果是自己主机的客户端服务器测试,可以使用127网段的
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:将套接字与服务器网络信息结构体绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        ERRLOG("bind error");
    }

    //第四步:将套接字设置为被动监听状态
    if(listen(sockfd, 5) == -1)
    {
        ERRLOG("listen error");
    }

NEXT:
    //第五步:阻塞等待客户端的连接
    if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) == -1)
    {
        ERRLOG("accept error");
    }

    //打印客户端的信息
    printf("客户端%s:%d连接了\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));

    while(1)
    {
RECV:
        //服务器接收客户端发送的内容
        if((bytes = recv(acceptfd, buf, N, 0)) == -1)
        {
            ERRLOG("recv error");
        }
        else if(bytes == 0)
        {
            printf("客户端%s-%d退出了\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
            goto NEXT;
        }

        //printf("filename: %s\n", buf);

        //判断文件是否存在
        if((fd = open(buf, O_RDONLY)) == -1)
        {
            //通过errno来获取文件不存在的错误码,将其单独操作
            //printf("errno = %d\n", errno);
            if(errno == ENOENT)
            {
                //如果文件不存在,服务器告知客户端
                if(send(acceptfd, "**NOEXIST**", N, 0) == -1)
                {
                    ERRLOG("send error");
                }
                
                goto RECV;
            }
            else 
            {
                ERRLOG("open error");
            }
        }

        //如果文件存在也要告知客户端
        if(send(acceptfd, "**EXIST**", N, 0) == -1)
        {
            ERRLOG("send error");
        }

        //TCP数据粘包问题
        //TCP底层有一个Nagel算法,会将一段时间内连续发送的内容组成
        //一个整体,然后再将其发送给接收方,但是接收方接收数据时没有
        //办法区分数据的类型,所以可能会将不同类型的数据一次性接收到,
        //代码就会出现冲突

        //处理方法1:将不能类型的数据通过延时处理,不将其放在一个数据包中发送
#if 0
        //读取文件内容并发送给客户端
        while((num = read(fd, buf, N)) != 0)
        {
            if(send(acceptfd, buf, num, 0) == -1)
            {
                ERRLOG("send error");
            }
        }

        sleep(1);

        //发送结束标志,告知客户端文件内容发送完毕
        if(send(acceptfd, "**OVER**", N, 0) == -1)
        {
            ERRLOG("send error");
        }
#endif

        //处理方法2:只要保证每次发送的数据包都一样大,就不会出现这个问题,
        //定义一个结构体,发送和接收结构体,这样保证每一个单独的数据包都一样大
        while((num = read(fd, buf, N)) != 0)
        {
            msg.n = num;
            strcpy(msg.text, buf);
            if(send(acceptfd, &msg, sizeof(msg), 0) == -1)
            {
                ERRLOG("send error");
            }
        }

        //发送结束标志,告知客户端文件内容发送完毕
        msg.n = 0;
        strcpy(msg.text, "**OVER**");
        if(send(acceptfd, &msg, sizeof(msg), 0) == -1)
        {
            ERRLOG("send error");
        }

        printf("文件发送完毕\n");
    }

    return 0;
}

1.2 客户端

//TCP网络编程之客户端

#include <stdio.h>  //atoi
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <arpa/inet.h>//字节序转换
#include <netinet/in.h>//ip

#include <sys/socket.h>//socket
#include <sys/types.h> //API接口//socket
#include <sys/stat.h>
#include <fcntl.h>

#define N 128
#define ERRLOG(errmsg) do{\
    perror(errmsg);\
    printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
    exit(1);\
}while(0)

//文件结构体
typedef struct{
    int n;  //文件内容的大小
    char text[N]; //文件内容
}MSG;

int main(int argc, char const *argv[])
{
    if(argc < 3)//判断终端输入的数量
    {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(1);
    }

/*  struct sockaddr_in {
        sa_family_t    sin_family; //address family: AF_INET (协议族:哪种协议方式)
        in_port_t      sin_port;   //port in network byte order (端口网络字节序)
        struct in_addr sin_addr;   //internet address 
    };

    //Internet address
    struct in_addr {
        uint32_t       s_addr;     //address in network byte order (地址网络字节序)
    }; */


    int sockfd;//定义文件描述符
    struct sockaddr_in serveraddr;//网络信息结构体
    socklen_t addrlen = sizeof(serveraddr);//网络信息结构体的大小
    char buf[N] = {0};//存放数据
    char filename[N] = {0};//文件名
    int fd;//文件描述符
    MSG msg;//文件结构体

    //第一步:创建套接字,协议族,套接字类型,附加协议
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //第二步:填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;//IPV4
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);//inet_addr:将点分十进制ip地址转换为网络字节序的无符号4字节整数
    serveraddr.sin_port = htons(atoi(argv[2]));//atoi:将数字型字符串转换为整形数据
                                               //htons:将主机字节序转化为网络字节序
    //第三步:给服务器发送客户端的连接请求
    if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        ERRLOG("connect error");
    }

    while(1)
    {
PRINTFILE:
        //客户端输入文件名并发送给服务器
        printf("请输入要下载的文件名: ");
        fgets(filename, N, stdin);
        filename[strlen(filename) - 1] = '\0';

        if(send(sockfd, filename, N, 0) == -1)
        {
            ERRLOG("send error");
        }

        //接收服务器发送的数据并做出相应的处理
        if(recv(sockfd, buf, N, 0) == -1)
        {
            ERRLOG("recv error");
        }

        //如果文件不存在,则重新输入
        if(strcmp(buf, "**NOEXIST**") == 0)
        {
            printf("文件%s不存在,请重新输入!!!\n", filename);
            goto PRINTFILE;
        }

        //如果文件存在,创建或者打开文件
        if((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
        {
            ERRLOG("open error");
        }

        //接收服务器发送的数据并写入到文件中
#if 0
        while((num = recv(sockfd, buf, N, 0)) != -1)
        {
            if(strcmp(buf, "**OVER**") == 0)
            {
                break;
            }

            write(fd, buf, num);
        }
#endif
        while(recv(sockfd, &msg, sizeof(msg), 0) != -1)
        {
            if(msg.n > 0)
            {
                write(fd, msg.text, msg.n);
            }
            else if(msg.n == 0)
            {
                break;
            }
        }
        printf("文件下载完毕\n");
    }

    return 0;
}

2 练习:机械臂控制

2.1 机械臂操作介绍

机械臂是由QT编写的TCP服务器代码,在哪运行,服务器ip地址就当前系统的地址,端口号任意设置机械臂协议构成:0xff 0x02 (1) (2) 0xff (1) 机械臂的摆臂 0x00 红色摆臂 0x01 蓝色摆臂 (2) 偏移值

注意:Qt机械臂程序的ip地址是你windows的ip地址,端口号可以任意设置

2.2 代码控制机械臂(demo)

//TCP网络编程之客户端

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

#define N 5
#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(1);
    }

    int sockfd;
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //第二步:填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:给服务器发送客户端的连接请求
    if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        ERRLOG("connect error");
    }

    //进行通信
    char buf[N] = {0xff, 2, 1, 90, 0xff};
    if(send(sockfd, buf, N, 0) == -1)
    {
        ERRLOG("send error");
    }

    return 0;
}

2.3 使用按键wasd控制机械臂(demo)

//TCP网络编程之客户端

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

#define N 5
#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(1);
    }

    int sockfd;
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //第二步:填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:给服务器发送客户端的连接请求
    if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        ERRLOG("connect error");
    }

    char buf_red[N] = {0xff, 2, 0, 0, 0xff};
    unsigned char buf_blue[N] = {0xff, 2, 1, 90, 0xff};

    if(send(sockfd, buf_red, N, 0) == -1)
    {
        ERRLOG("send error");
    }

    if(send(sockfd, buf_blue, N, 0) == -1)
    {
        ERRLOG("send error");
    }

    char ch;

    while(1)
    {
        ch = getchar();
        getchar();

        switch(ch)
        {
        case 'a':
            if(buf_red[3] != -90)
            {
                buf_red[3] -= 5;
            }
             
            if(send(sockfd, buf_red, N, 0) == -1)
            {
                ERRLOG("send error");
            }
            break;
        case 'd':
            if(buf_red[3] != 90)
            {
                buf_red[3] += 5;
            }
             
            if(send(sockfd, buf_red, N, 0) == -1)
            {
                ERRLOG("send error");
            }
            break;
        case 'w':
            if(buf_blue[3] != 0)
            {
                buf_blue[3] -= 5;
            }
             
            if(send(sockfd, buf_blue, N, 0) == -1)
            {
                ERRLOG("send error");
            }
            break;
        case 's':
            if(buf_blue[3] != 180)
            {
                buf_blue[3] += 5;
            }
             
            if(send(sockfd, buf_blue, N, 0) == -1)
            {
                ERRLOG("send error");
            }
            break;
        }
    }

    return 0;
}

2.4 linux键盘编程(demo)

驱动工程师编写键盘驱动,会在/dev/input目录下生成一个event0-event4字符设备文件,当按下键盘上的某一个按键时,会往设备文件中写内容,所以我们需要从设备文件中读取内容并解析,然后找到想要使用的按键,首先测试自己的键盘驱动是哪一个文件:sudo cat event0-4 我们发现读取到的内容时二进制的,所以需要查询资料找到读取的内容到底是什么,通过查询资料我们得知,读取到的内容是linux/input.h文件中的一个结构体

struct input_event {
    struct timeval time;
    __u16 type;  类型
    EV_KEY  键盘
    __u16 code;
    __s32 value;  按键事件的类型值
        1  按键按下
        0  按键松开
        2  按键一直按下
};
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/input.h>

#define DEVFILE "/dev/input/event1"

int main(int argc, char const *argv[])
{
    //打开键盘对应的设备文件
    int fd;
    if((fd = open(DEVFILE, O_RDONLY)) == -1)
    {
        perror("open error");
        exit(1);
    }

    //读取设备文件的内容并解析
    struct input_event mykey;
    while(1)
    {
        read(fd, &mykey, sizeof(mykey));

        if(mykey.type == EV_KEY)
        {
            if(mykey.value == 1)
            {
                printf("%d按键按下\n", mykey.code);
            }
            else if(mykey.value == 0)
            {
                printf("%d按键松开\n", mykey.code);
            }
            else if(mykey.value == 2)
            {
                printf("%d按键一直按下\n", mykey.code);
            }
            //printf("type:%d, value:%d, code:%d\n", mykey.type, mykey.value, mykey.code);
        }
    }
    
    return 0;
}

2.5 使用按键wasd控制机械臂(高级版)(demo)

//TCP网络编程之客户端

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h> 
#include <string.h>
#include <linux/input.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define DEVFILE "/dev/input/event1"

#define N 5
#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

int main(int argc, char const *argv[])
{
    if(argc < 3)
    {
        fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
        exit(1);
    }

    int sockfd;
    struct sockaddr_in serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //第二步:填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(atoi(argv[2]));

    //第三步:给服务器发送客户端的连接请求
    if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        ERRLOG("connect error");
    }

    char buf_red[N] = {0xff, 2, 0, 0, 0xff};
    unsigned char buf_blue[N] = {0xff, 2, 1, 90, 0xff};

    if(send(sockfd, buf_red, N, 0) == -1)
    {
        ERRLOG("send error");
    }

    if(send(sockfd, buf_blue, N, 0) == -1)
    {
        ERRLOG("send error");
    }

    char ch;
    int fd;
    if((fd = open(DEVFILE, O_RDONLY)) == -1)
    {
        perror("open error");
        exit(1);
    }

    struct input_event mykey;
    while(1)
    {
        read(fd, &mykey, sizeof(mykey));

        if(mykey.type == EV_KEY && mykey.value == 1)
        {
            switch(mykey.code)
            {
            case 30: //a
                if(buf_red[3] != -90)
                {
                    buf_red[3] -= 5;
                }
                
                if(send(sockfd, buf_red, N, 0) == -1)
                {
                    ERRLOG("send error");
                }
                break;
            case 32: //d
                if(buf_red[3] != 90)
                {
                    buf_red[3] += 5;
                }
                
                if(send(sockfd, buf_red, N, 0) == -1)
                {
                    ERRLOG("send error");
                }
                break;
            case 17: //w
                if(buf_blue[3] != 0)
                {
                    buf_blue[3] -= 5;
                }
                
                if(send(sockfd, buf_blue, N, 0) == -1)
                {
                    ERRLOG("send error");
                }
                break;
            case 31: //s
                if(buf_blue[3] != 180)
                {
                    buf_blue[3] += 5;
                }
                
                if(send(sockfd, buf_blue, N, 0) == -1)
                {
                    ERRLOG("send error");
                }
                break;
            }
        }
    }
    return 0;
}

3 实现TFTP客户端的下载功能

3.1客户端实现上传和下载

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                        }while(0)

#define N 128

void do_help()
{
    system("clear");
    printf("---------------------\n");
    printf("------ 1. 下载 ------\n");
    printf("------ 2. 上传 ------\n");
    printf("------ 3. 退出 ------\n");
    printf("---------------------\n");
}

void do_download(int sockfd, struct sockaddr_in serveraddr)
{
    char filename[N] = {};
    printf("请输入要下载的文件名:");
    scanf("%s", filename);

    char data[1024] = "";
    int data_len;
    int fd;
    int flags = 0;
    int num = 0;
    int recv_len;

    //组数据并发送
    data_len = sprintf(data, "%c%c%s%c%s%c", 0, 1, filename, 0, "octet", 0);
    if(sendto(sockfd, data, data_len, 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        ERRLOG("fail to sendto");
    }

    //接收数据并分析处理
    socklen_t addrlen = sizeof(serveraddr);
    while(1)
    {
        if((recv_len = recvfrom(sockfd, data, sizeof(data), 0, (struct sockaddr *)&serveraddr, &addrlen)) < 0)
        {
            ERRLOG("fail to recvfrom");
        }

        //printf("%d - %u\n", data[1], ntohs(*(unsigned short *)(data + 2)));
        //printf("%s\n", data + 4);

        if(data[1] == 5)
        {
            printf("error: %s\n", data + 4);
            return ;
        }
        else if(data[1] == 3)
        {
            //防止文件内容清空
            if(flags == 0)
            {
                if((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664)) < 0)
                {
                    ERRLOG("fail to open");
                }
                flags = 1;
            }

            //判断数据包的编号是否是上一次的编号加1
            if(num + 1 == ntohs(*(unsigned short *)(data + 2)) && recv_len == 516)
            {
                //向文件写入数据
                write(fd, data + 4, recv_len - 4);
            
                //组数据发送给服务器
                data[1] = 4; 
                if(sendto(sockfd, data, 4, 0, (struct sockaddr *)&serveraddr, addrlen) < 0)
                {
                    ERRLOG("fail to sendto");
                }

                num = ntohs(*(unsigned short *)(data + 2));
            }
            //接收到的最后一次的数据
            else if(num + 1 == ntohs(*(unsigned short *)(data + 2)) && recv_len < 516)
            {
                write(fd, data + 4, recv_len - 4);
                break;
            }
        }
    }

    printf("文件下载成功\n");
}

void do_upload(int sockfd, struct sockaddr_in serveraddr)
{
    char filename[N] = {};
    printf("请输入要上传的文件名:");
    scanf("%s", filename);

    //打开文件并判断文件是否存在
    int fd;
    if((fd = open(filename, O_RDONLY)) < 0)
    {
        if(errno == ENOENT)
        {
            printf("文件%s不存在,请重新输入\n", filename);
            return ;
        }
        else
        {
            ERRLOG("fail to open");
        }
    }

    //组数据并发送给服务器执行上传功能
    char data[1024] = {};
    int data_len;
    socklen_t addrlen = sizeof(serveraddr);

    data_len = sprintf(data, "%c%c%s%c%s%c", 0, 2, filename, 0, "octet", 0);

    if(sendto(sockfd, data, data_len, 0, (struct sockaddr *)&serveraddr, addrlen) < 0)
    {
        ERRLOG("fail to sendto");
    }

    //接收服务器发送的数据并分析处理
    int recv_len;
    int num = 0;
    ssize_t bytes;
    while(1)
    {
        if((recv_len = recvfrom(sockfd, data, sizeof(data), 0, (struct sockaddr *)&serveraddr, &addrlen)) < 0)
        {
            ERRLOG("fail to recvfrom");
        }

        //printf("%d - %d\n", data[1], ntohs(*(unsigned short *)(data + 2)));
        //printf("%s\n", data + 4);

        if(data[1] == 4 && num == ntohs(*(unsigned short *)(data + 2)))
        {
            num++;
            bytes = read(fd, data + 4, 512);
            data[1] = 3;
            *(unsigned short *)(data + 2) = htons(num);

            if(bytes == 512)
            {
                if(sendto(sockfd, data, bytes + 4, 0, (struct sockaddr *)&serveraddr, addrlen) < 0)
                {
                    ERRLOG("fail to sendto");
                }
            }
            else
            {
                if(sendto(sockfd, data, bytes + 4, 0, (struct sockaddr *)&serveraddr, addrlen) < 0)
                {
                    ERRLOG("fail to sendto");
                }
                break;
            } 
        }
    }

    printf("文件上传完毕\n");
}

int main(int argc, char const *argv[])
{
    int sockfd;
    struct sockaddr_in serveraddr;
    //创建套接字
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        ERRLOG("fail to socket");
    }

    //填充服务器网络信息结构体
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    serveraddr.sin_port = htons(69);

    system("clear");
    printf("------------------------------\n");
    printf("----请输入help查看帮助信息----\n");
    printf("------------------------------\n");
    printf(">>> ");

    char buf[N] = {};
NEXT:
    fgets(buf, N, stdin);
    buf[strlen(buf) - 1] = '\0';
    if(strncmp(buf, "help", 4) == 0)
    {
        do_help();
    }
    else 
    {
        printf("您输入的有误,请重新输入\n");
        goto NEXT;
    }

    int num;
    while(1)
    {
        printf("input>>> ");
        scanf("%d", &num);
        switch (num)
        {
        case 1:
            do_download(sockfd, serveraddr);
            break;
        case 2:
            do_upload(sockfd, serveraddr);
            break;
        case 3:
            close(sockfd);
            exit(0);
            break;
        default:
            printf("您输入的有误,请重新输入\n");
            break;
        }
    }

    return 0;
}