基于UDP协议的接收和发送实验

基于UDP协议的接收和发送实验

选修了一门TCP/IP协议分析及应用,做了几个小实验,这里把实验的原理、自己的代码和经验教训和大家分享一下。

实验中的全部代码托管在Github上,请通过 fork + pull request 方法来帮助改进项目。

  • 基于UDP协议的接收和发送实验
        • 导语
    • 一实验内容
    • 二UDP编程框架
      • 1 UDP服务器编程框架
      • 2 UDP客户端编程框架
    • 三UDP协议程序设计的常用函数
      • 1 建立套接字socket 和绑定套接字bind
      • 2 接收函数recvfromrecv
      • 3 发送函数sendtosend
    • 四实验代码实现
      • 1 UDP客户端 dupcli01cpp
      • 2 UDP服务器端 udpserv01cpp
    • 五程序扩展实现
    • 六程序运行结果

导语:

UDP协议是User Datagram Protocol 的简称,他是无连接的,不可靠的网络协议。
本实验目的是使用因特网提供的UDP传输协议,实现一个简单的UDP客户/服务器程序,以了解传输层所提供的UDP服务的特点,应用层和传输层之间的软件接口风格,熟悉socket机制和UDP客户端/服务器方式程序的结构。
本文在介绍UDP协议收发技术的同时,提供了相关代码,并将笔者在debug过程中的经验和教训带给读者。

一,实验内容

设计与实现UDP echo客户及服务器程序,完成以下功能:
客户从标准输入读一行文本,写到服务器上;服务器从网络输入读取此行,并回射(echo)给客户;客户读此回射行,并将其写到标准输出。
扩展一下三个内容
* 在客户机上显示服务器的目录
* 将服务器指定的文件下载到客户机
* 讲客户机指定的文件上传到服务器

二,UDP编程框架

使用UDP进行程序设计可以分为客户端和服务器端两个部分。服务器端主要包含建立套接字、将套接字与地址结构进行帮顶、读写数据、关闭套接字几个过程。客户端包括建立套接字、读写数据、关闭套接字几个过程。服务器端和客户端两个流程志坚的主要差别在于对地址的绑定(bind())函数,客户端可以不用进行地址和端口的绑定操作。

2.1 UDP服务器编程框架

上图中对UDP协议服务器程序框架进行了说明,服务器流程主要分为下述6个部分吗,即建立套接字、设置套接字地址参数、进行端口绑定、接收数据、发送数据、关闭套接字。
1. 建立套接字文件描述符,使用socket(), 生成套接字文件描述符,例如:

int s = socket(AF_INET, SOCK_DGRAM, 0);

建立一个AF_INET 族的数据包套接字,UDP协议的套接字使用SOCK_DGRAM选项。
2. 设置服务器地址和侦听端口,初始化要绑定的网络地址结构,例如:

struct sockaddr addr_serv;
addr_serv.sin_family = AF_INET;                     //地址类型为AF_INET
addr_serv.sin.addr.s_addr = htonl(INADDR_ANY);      //任意本机地址
addr_serv.sin_port = htons(PORT_SERV);              //服务器端口

地址结构的类型为AF_INEF ; IP地址为任意的本地地址; 服务器的端口为用户定义的端口地址; 注意成员sin_addr.s_addrsin_port均为网络字节序。
3. 绑定侦听端口,使用bind()函数,将套接字文件描述符和一个地址类型变量进行绑定,例如

bind(s,(struct sockaddr*) &addr_serv,sizeof(addr_serv));    //绑定地址
  1. 接收客户端的数据,使用recvfrom()函数接受客户端的网络数据。
  2. 向客户端发送数据,是应用sendto()函数向服务器主机发送数据。
  3. 关闭套接字,使用close()函数释放资源

2.2 UDP客户端编程框架

在上图中,同样对UDP协议的客户端流程进行了描述,按照图中所示,UDP协议的客户端流程分为套接字建立、设置目的的地址和端口、向服务器发送数据、从服务器接收数据、关闭套接字5个部分。与服务器的框架相比,少了bind()部分,客户端程序的端口和本地的地址可以由系统使用时指定,在使用sendto()recvfrom()的时候,网络协议栈会临时指定本地的端口和地址,流程如下:
1. 建立套接字文件描述符,socket()
2. 设置服务器地址和端口,struct sockaddr
3. 向服务器地址和端口,sendto();
4. 接收服务器的数据,recvfrom();
5. 关闭套接字,close();

三,UDP协议程序设计的常用函数

UDP协议常用的函数有recv() /recvform()send()/ sendto()socket()bind() 等。当然这些函数同样可以用于TCP协议的程序设计。

3.1 建立套接字socket() 和绑定套接字bind()

UDP协议使用建立套接字的方式和TCP方式一样,使用socket() 函数,只不过协议的类型描述符使用SOCK_DGRAM, 而不是参数SOCK_STREAM。 例如下面是一个建立UDP套接字文件描述符的代码。

int s;
s = socket(AF_INEF, SOCK_DGRAM, 0);

UDP协议使用bind()函数的方法与TCP没有什么差别,将一个套接字描述符与一个地址结构绑定在一起。例如,下面的代码将一个本地的地址和套接字文件描述符绑定在在了一起。

struct sockaddr_in local;                               //本地的地址信息
int from_len = sizeof(from);                            //地址结构的长度
local. sin _family = AF_INET;                           //协议簇
local. sin _port = htons(8888);                         //本地端口
local. sin _addr.s_addr = htonl(INADDR_ANY);            //任意本机地址
s = socket(AF_INET, SOCK_DGRAM, 0);         //初始化一个IPv4族的数据包套接字
if (s == -1) {                              //检查是否正常初始化socket
    perror("socket");
    exit(EXIT_FAILURE);
}
bind(s, (struct sockaddr*) &local, sizeof(local));      //套接字绑定

绑定函数bind()使用的时机,即什么时候需要绑定需要介绍一下。函数bind()的作用是将一个套接字文件描述符和一个本地地址绑定在一起,即把发送数据的端口地址和IP地址进行了制定。例如在发送数据的时候,如果不进行绑定,则会临时选择一个随机的端口。

3.2 接收函数recvfrom()/recv()

当客户端成功建立了一个套接字文件描述符并建立了合适的strcut sockaddr结构后,或者服务器端成功将套接字文件描述符和地址结构绑定后,可以使用recv()故意整个recvfrom()来接收到达此套接字文件描述符上的数据或者在这个套接字文件描述符上的等待数据的到来。

recv()函数和recvfrom()函数的原型如下:

#include 
#include 
ssize_t recv(int s, void *buff, size_t len, int flags);
ssize_t recvfrom(int s, void *buff,size_t len, int flags, struct sockaddr *from, socklen_t *from)

3.3 发送函数sendto()/send()

当客户端成功建立了一个套接字文件描述符,并构建了合适的struct sockaddr结构后,或者服务器成功地将套接字文件描述符和地址结构绑定后,可以使用send()或者sendto()函数来发送数据到某个主机上。

send()函数和sendto()函数的原型如下:

#include 
#include 
ssize_t send(int s, const void *buff, size_t len, int flags);
ssize_t sento(int s, const void *buff, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);

四,实验代码实现

4.1 UDP客户端 dupcli01.cpp

  • 首先给出udp client端的代码,标准的实现方法,比较简单。
/*
 * udpcli01.cpp
 *
 *  Created on: 2015年4月23日
 *      Author: gzxultra
 */

    #include        
    #include        
    #include        
    #include        
    #include        
    #include        
    #include        
    #define MAXLINE                  4096
    #define LISTENQ                  1024    /* 2nd argument to listen() */
    #define SERV_PORT                9877
    #define SA      struct sockaddr

    void dg_cli(FILE *, int, const SA *, socklen_t);

    int
    main(int argc, char **argv)
    {
        int sockfd;
        struct sockaddr_in servaddr;

        if (argc != 2) {
            printf("usage:udpcli01sigio \n");
            exit(1);
        }

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(SERV_PORT);

        inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

        if((sockfd = socket(AF_INET, SOCK_DGRAM, 0))<0) {
            printf("socket error.\n");
            exit(1);
        }

        dg_cli(stdin, sockfd, (SA *)&servaddr, sizeof(servaddr));

        exit(0);
    }

    void
dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
    int  n;
    char sendline[MAXLINE], recvline[MAXLINE + 1];

    while (fgets(sendline, MAXLINE,fp) != NULL) {
        sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
        n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
        recvline[n] = 0; /* null terminate */
        fputs(recvline, stdout);
    }
}

4.2 UDP服务器端 udpserv01.cpp

  • 在实现服务器的代码时我却遇到了不少的问题。
  • 如果严格按照之前所说的,“建立套接字、将套接字与地址结构进行绑定、读写数据、关闭套接字”几个过程,相信很快便能写完server的代码。
  • 可事实上我这里纠结了三天才完成,问题不断。


    如图所示,因为client总是输出莫名其妙的字符串(以前输入字符的缓存、乱码),我在server增加了console输出,来检查recvfrom函数是否成功且正确地接收到来自client的字符串。令人失望的是,server的根本没有console输出,这表明udp两端没有完成通信,server阻塞在recvfrom函数上。
  • 随着不断的调试,我发现每次重启环境的第一次输出总是正确的,随后就是乱码。
  • 我习惯写完代码之后用Sublime编译一下,按下Command+B,然后再在terminal里./udpserv01,实际上Sublime自己运行了一次程序(开了一个服务器进程)
    这样就导致同一时间有两个server在运行,于是client端无法正确发送!
    如果老老实实terminal编译就不会浪费这么多的时间处理这样低级的、无效的错误。


* 这里以亲身经历提醒各位同学,学习阶段就不要怕麻烦,规范的编程编译习惯受益终身。
*另外感谢v2ex的carto同学指出的错误,客户端传给服务端的字符串不带0,服务端取长度前要加0。

/*
 * udpserv01.cpp
 *
 *  Created on: 2015年4月23日
 *      Author: gzxultra
 */

#include        
#include        
#include        
#include        
#include        
#include        
#define MAXLINE                  4096
#define LISTENQ                  1024    /* 2nd argument to listen() */
#define SERV_PORT                9877
#define SA      struct sockaddr
#include    


static int sockfd;
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg);
void GetList(int sockfd, SA *pcliaddr, socklen_t clilen) ;

int main(int argc, char ** argv) {

    struct sockaddr_in servaddr, cliaddr;

    //使用socket()函数生成套接字文件描述符
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("socket error.\n");
        exit(1);
    }

    //设置服务器地址和侦听端口,初始化要绑定的网络地址结构
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;                      //地址类型为AF_INET
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);       //任意本地地址
    servaddr.sin_port = htons(SERV_PORT);               //服务器端口

    //绑定侦听端口
    bind(sockfd, (SA *) &servaddr, sizeof(servaddr));

    char instruction[3][10] = {
        "getlist\n",
        "download\n",
        "upload\n"
    };

    int op = 0;
    int i = 0,j = 0;
    int n = 0;
    char mesg[MAXLINE];
    socklen_t len = sizeof(cliaddr);
    //printf("running here!\n");
    while(1){
        //len = clilen;
        memset(mesg,0,sizeof(mesg));
        //recvfrom接收客户端的网络数据
        n = recvfrom(sockfd, mesg, MAXLINE, 0, (SA *) &cliaddr,  &len);


        op = 0;

        //处理接受到的回车符
        for(j=0;j<=n;j++){
            if(mesg[j] == '\n') mesg[j] = '\0';
            break;
        }
        for(i = 1;i<=3;i++)
            if(strcmp(mesg,instruction[i-1])== 0){
                op = i;
                break;
            }   //choose operation
        for(j=0;j<=n;j++){
            if(mesg[j] == '\0') mesg[j] = '\n'; 
            break;
        }

        printf("op = %d\n",op);
        switch(op){
            case 1: 
                GetList(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
                break;
            //case 2:DownLoad();break;
            //case 3:UpLoad();break;
            default:{
                dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr), mesg);
                break;
            }
        }
        //dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr), mesg);
        //printf("echo done!\n");
    }
}

void GetList(int sockfd, SA *pcliaddr, socklen_t clilen) {
    int n;
    socklen_t len;

    printf("catch\n");

    //char mesg[MAXLINE];

    char strlin[100][MAXLINE];  //一百行缓冲区
    char sendline[MAXLINE]={};  //最终发送的字符串

    int i = 0;
    FILE *fp;


            system("ls -l>filelog.txt");    //输出重定向

            //printf("2done!\n");

            ssize_t read;
            //char c;
            //int count=0;
            fp = fopen("filelog.txt", "r");

            if (!fp) {
                printf("get ls order failed!");
            } else {
                //printf("4done!\n");
                //printf("%p\n", fp);
                while (1) {
                    //printf("2done!!!!\n");
                    if (NULL==fgets(strlin[i], MAXLINE, fp))
                        break;  //如果读到文件尾,结束读取
                    //strlin[i][strlen(strlin[i] - 1)] = '\0';  //每行加\0结尾
                    i++;    //写成strlin[i++]提示i未定义,gcc的bug??
                }
                /*
                 ++i;
                 memcpy(strlin[i],"EOF",sizeof("EOF"));
                 */
                for (int n = 0; n < i; n++){
                    strcat(sendline, strlin[n]);
                    //strcat(sendline,"\n");
                }
                //sendto(sockfd, strlin, sizeof(strlin), 0, pcliaddr, len);
                fputs(sendline,stdout);
                sendto(sockfd, sendline, strlen(sendline)+1, 0, pcliaddr, clilen);
            }
            //strcat(sendline, '\0');

            fclose(fp);

            printf("FileList Get!\n");

}

void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg) {
    //fputs(mesg,stdout);
    //printf("\n");
    sendto(sockfd, mesg, strlen(mesg)+1, 0, pcliaddr, clilen);

}

五,程序扩展实现

题目有三个扩展功能需要实现
1. 获取服务器的目录
2. 从服务器下载指定文件到客户机
3. 从客户机上载指定文件到服务器

三个扩展题的实现思路本质上是一样的,在服务器/客户机打开一个文档,按行读到尾部封装到套接字里一起发送,客户机/服务器收到后按行写入一个文件里。
其中第一个题目需要将system("ls -l")的输出重定向到本地一个文本文件里,再读取发送。

附上完整的实现代码。

/*
 * udpcli01.cpp
 *
 *  Created on: 2015年4月23日
 *      Author: gzxultra
 *  Finished on: 2015年4月30日
 */

#include        
#include        
#include        
#include        
#include        
#include        
#include        
#define MAXLINE                  4096
#define LISTENQ                  1024    /* 2nd argument to listen() */
#define SERV_PORT                9877
#define SA      struct sockaddr

void dg_cli(FILE *, int, const SA *, socklen_t);

int main(int argc, char **argv) {
    int sockfd;
    struct sockaddr_in servaddr;

    if (argc != 2) {
        printf("usage:udpcli01sigio \n");
        exit(1);
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);

    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("socket error.\n");
        exit(1);
    }

    dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));

    exit(0);
}

void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
{
    int n;
    char sendline[MAXLINE], recvline[MAXLINE + 1];
    char instruction[3][10] = {
        "getlist\n",
        "download\n",
        "upload\n"
    };
    int i=0;
    int op =0;
    FILE *CopyToLocal;
    FILE *Uploadfp;

    while (fgets(sendline, MAXLINE,fp) != NULL) {
        sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
        n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
        recvline[n] = 0; /* null terminate */
        //fputs(recvline,stdout);
        op = 0;

        for(i = 1;i<=3;i++)
        if(strcmp(sendline,instruction[i-1])== 0) {
            op = i;
            break;
        }   //choose operation
        printf("op=%d\n",op);

        switch(op) {
            case 1: {
                fputs(recvline, stdout);
                break;
            }
            case 2: {
                fputs(recvline, stdout);
                fgets(sendline, MAXLINE,fp);        //给出上传文件的文件名
                sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
                memset(recvline,0,sizeof(recvline));
                n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
                recvline[n] = 0; /* null terminate */

                char path[MAXLINE] = "/Users/gzxultra/downloads/";
                strcat(path,sendline);
                fputs(path,stdout);
                printf("\n");
                CopyToLocal = fopen(path,"w");
                if(!CopyToLocal)
                printf("download file failed!");
                //fputs(recvline,stdout);
                fputs(recvline, CopyToLocal);
                //fputs(recvline,stdout);
                //printf("download !");
                fclose(CopyToLocal);
                break;
            }

            case 3: {
                fputs(recvline, stdout);        //应该是提示输入文件名
                fgets(sendline, MAXLINE,fp);//应该是输入要上传的文件名字吧?
                sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
                memset(recvline,0,sizeof(recvline));
                //recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); //接收一個okay
                //recvline[n] = 0;// null terminate 

                //fputs(sendline,stdout);
                //fputs("\n",stdout);

                for(int j=0;j<=n;j++) {
                    if(sendline[j] == '\n') sendline[j] = '\0';
                    //break;
                }
                //fputs(sendline,stdout);
                //printf("\n");
                Uploadfp = fopen(sendline, "r");
                int i =0;
                char strlin[1000][MAXLINE];
                //printf("don't\n");
                if (!Uploadfp) {
                    printf("get file failed!\n");

                } else {
                    //printf("4done!\n");
                    //printf("%p\n", fp);
                    while (1) {
                        //printf("2done!!!!\n");
                        if (NULL==fgets(strlin[i], MAXLINE, Uploadfp))
                        break;//如果读到文件尾,结束读取
                        //strlin[i][strlen(strlin[i] - 1)] = '\0';  //每行加\0结尾
                        i++;//写成strlin[i++]提示i未定义,gcc的bug??
                    }

                    memset(sendline,0,sizeof(sendline));
                    for (int n = 0; n < i; n++) {
                        strcat(sendline, strlin[n]);
                        //strcat(sendline,"\n");
                    }
                    //sendto(sockfd, strlin, sizeof(strlin), 0, pcliaddr, len);
                    //fputs(sendline,stdout);
                    sendto(sockfd, sendline, strlen(sendline)+1, 0, pservaddr, servlen);

                    //recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
                    //fputs(recvline,stdout);//path
                    //fputs("\n",stdout);
                }
                //strcat(sendline, '\0');

                fclose(Uploadfp);
            }

            default: {
                fputs(recvline, stdout);
                break;
            }
            //to spilit up the output by ***

        }
        for(int i=0;i<25;i++) {
            printf("*");
        }
        printf("\n");
    }

}

/*
 * udpserv01.cpp
 *
 *  Created on: 2015年4月23日
 *      Author: gzxultra
 *  Finished on: 2015年4月30日
 */

#include        
#include        
#include        
#include        
#include        
#include        
#define MAXLINE                  4096
#define LISTENQ                  1024    /* 2nd argument to listen() */
#define SERV_PORT                9877
#define SA      struct sockaddr
#include    

static int sockfd;
void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg);
void GetList(int sockfd, SA *pcliaddr, socklen_t clilen);
void DownLoad(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg);
void UpLoad(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg);

int main(int argc, char ** argv) {

    struct sockaddr_in servaddr, cliaddr;

    //使用socket()函数生成套接字文件描述符
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("socket error.\n");
        exit(1);
    }

    //设置服务器地址和侦听端口,初始化要绑定的网络地址结构
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;                      //地址类型为AF_INET
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);       //任意本地地址
    servaddr.sin_port = htons(SERV_PORT);               //服务器端口

    //绑定侦听端口
    bind(sockfd, (SA *) &servaddr, sizeof(servaddr));

    char instruction[3][10] = { "getlist\n", "download\n", "upload\n" };

    int op = 0;
    int i = 0, j = 0;
    int n = 0;
    char mesg[MAXLINE];
    socklen_t len = sizeof(cliaddr);
    //printf("running here!\n");
    while (1) {
        //len = clilen;
        memset(mesg, 0, sizeof(mesg));
        //recvfrom接收客户端的网络数据
        n = recvfrom(sockfd, mesg, MAXLINE, 0, (SA *) &cliaddr, &len);

        op = 0;

        //处理接受到的回车符

        for (i = 1; i <= 3; i++)
            if (strcmp(mesg, instruction[i - 1]) == 0) {
                op = i;
                break;
            }   //choose operation

        printf("op = %d\n", op);
        switch (op) {
            case 1:{
                GetList(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
                break;
            }
            case 2: {
                DownLoad(sockfd, (SA *) &cliaddr, sizeof(cliaddr), mesg);
                break;
            }
            case 3: {
                UpLoad(sockfd, (SA *) &cliaddr, sizeof(cliaddr), mesg);
                break;
            }
            default: {
                dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr), mesg);
                break;
            }
        }//switch(op)
    }//while
}//main

void dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg) {
    //fputs(mesg,stdout);
    //printf("\n");
    sendto(sockfd, mesg, strlen(mesg) + 1, 0, pcliaddr, clilen);

}

void GetList(int sockfd, SA *pcliaddr, socklen_t clilen) {
    int n;
    socklen_t len;

    printf("catch\n");

    char strlin[100][MAXLINE];  //一百行缓冲区
    char sendline[MAXLINE] = { };   //最终发送的字符串

    int i = 0;
    FILE *fp;

    system("ls -l>filelog.txt");    //输出重定向

    ssize_t read;

    fp = fopen("filelog.txt", "r");

    if (!fp) {
        printf("get ls order failed!");
    } else {
        while (1) {
            //printf("2done!!!!\n");
            if (NULL == fgets(strlin[i], MAXLINE, fp))
                break;  //如果读到文件尾,结束读取
            //strlin[i][strlen(strlin[i] - 1)] = '\0';  //每行加\0结尾
            i++;    //写成strlin[i++]提示i未定义,gcc的bug??
        }

        for (int n = 0; n < i; n++) {
            strcat(sendline, strlin[n]);
            //strcat(sendline,"\n");
        }
        //sendto(sockfd, strlin, sizeof(strlin), 0, pcliaddr, len);
        fputs(sendline, stdout);
        sendto(sockfd, sendline, strlen(sendline) + 1, 0, pcliaddr, clilen);
    }
    //strcat(sendline, '\0');

    fclose(fp);

    printf("FileList Get!\n");

}

void DownLoad(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg) {
    char text[MAXLINE] = "Please input the file name you'd like to download\n";
    char sendline[100 * MAXLINE];
    char instr[] = "download\n";
    int n = 0;
    //fputs(mesg,stdout);
    while (strcmp(mesg, instr) == 0) {

        printf("start download\n");
        sendto(sockfd, text, strlen(text) + 1, 0, pcliaddr, clilen);
        n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &clilen);
    }
    FILE *Downloadfp;
    fputs(mesg, stdout);
    //fputs("\n",stdout);

    for (int j = 0; j <= n; j++) {
        if (mesg[j] == '\n')
            mesg[j] = '\0';
        //break;
    }
    Downloadfp = fopen(mesg, "r");
    int i = 0;
    char strlin[1000][MAXLINE];
    //printf("don't\n");
    if (!Downloadfp) {
        printf("get file failed!\n");

    } else {
        while (1) {
            //printf("2done!!!!\n");
            if (NULL == fgets(strlin[i], MAXLINE, Downloadfp))
                break;  //如果读到文件尾,结束读取
            //strlin[i][strlen(strlin[i] - 1)] = '\0';  //每行加\0结尾
            i++;    //写成strlin[i++]提示i未定义,gcc的bug??
        }
        memset(sendline, 0, sizeof(sendline));
        for (int n = 0; n < i; n++) {
            strcat(sendline, strlin[n]);
            //strcat(sendline,"\n");
        }
        //sendto(sockfd, strlin, sizeof(strlin), 0, pcliaddr, len);
        fputs(sendline, stdout);
        sendto(sockfd, sendline, strlen(sendline) + 1, 0, pcliaddr, clilen);
    }
    //strcat(sendline, '\0');

    fclose(Downloadfp);

    printf("file download!\n");
}

void UpLoad(int sockfd, SA *pcliaddr, socklen_t clilen, char *mesg) {
    char text[MAXLINE] = "Please input the file name you'd like to upload\n";
    char recvline[100 * MAXLINE];
    char instr[] = "upload\n";
    int n = 0;
    //fputs(mesg,stdout);

    FILE *CopyToServer;

    while (strcmp(mesg, instr) == 0) {
        printf("start upload\n");
        sendto(sockfd, text, strlen(text) + 1, 0, pcliaddr, clilen);
        n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &clilen);
    }   //上面应该没问题了~

    char path[MAXLINE] = "/Users/gzxultra/";
    strcat(path, mesg);
    CopyToServer = fopen(path, "w");
    if (!CopyToServer)
        printf("Upload file failed!\n");
    else {
        recvfrom(sockfd, recvline, 100 * MAXLINE, 0, pcliaddr, &clilen);
        fputs(recvline, CopyToServer);
        //sendto(sockfd, path, strlen(path) + 1, 0, pcliaddr, clilen);
    }
    //fputs(recvline,stdout);

    //fputs(recvline,stdout);
    //printf("download !");
    fclose(CopyToServer);
    printf("file Uploadload!\n");
}

六,程序运行结果

执行程序,
* 输入getlist获取服务器的目录
* 下载服务器文件filelog.txt
* 上载客户机文件daytimecli.c

你可能感兴趣的:(基于UDP协议的接收和发送实验)