北京大学计算机网络lab1——MyFTP

这篇具有很好参考价值的文章主要介绍了北京大学计算机网络lab1——MyFTP。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

Lab目标

一、知识补充

二、具体实现

1.数据报文格式和字符串处理

2.open函数

3.auth

4.ls

5.get和put

三、总结


请同学们仅以此作为借鉴,,请务必自己先试着写写,然后遇到问题再看看文章。听闻我的文章间接导致了一部分同学被发了查重邮件,请大家务必谨慎再谨慎,不必操之过急。我本人在信科水平算是非常差的,写这篇文章当初纯粹是为了找工作,放在简历上显得自己很勤奋,有经常做总结的习惯(实际并没有),让hr和老板们看着觉得我还不错而已(实际挺拉的)。我这样的水平,计网的lab1也只用了3天就完全完成了(中间熬了个夜到4点想速成,结果脑子不清醒写了一坨,第二天删了重来反而很快就过了),这中间的一些坑(比如转大小端)我也会去问问室友,也都能很快解决。所以希望大家也能有所收获。

ps:本人靠着计网lab几乎就足够在就业行情并不好的23年找到自己满意的工作了,计网lab的教程也非常给力,对我这种恐惧写lab的菜狗都非常友好(本人写lab3确实比较痛苦,因为没什么人可以问)。所以大家一定要珍惜这次机会。共勉!

Lab目标

简介:MyFTP是我们为了方便同学们快速理解POSIX API设计的一个简单的Lab,在这个Lab中你需要完成一个简单的FTP Server和FTP Client CLI(CLI指命令行界面)

  1. MyFTP的Client支持以下的命令
    • open <IP> <port>
    • auth <username> <password>:向对侧进行身份验证
    • ls:获取对方当前运行目录下的文件列表,一个样例输出如下
    • get <filename>:将Server运行目录中的<filename>文件存放到Client运行目录的<filename>中
    • put <filename>:将Client运行目录中的<filename>文件存放到Server运行目录的<filename>中
    • quit:如果有连接则先断开,后关闭Client
  2. MyFTP的Server需要支持如下的功能特点
    • 权限控制:用户需要登录这里简化为用户名为user,密码为123123
    • 获取文件列表:这里文件列表由指令ls生成,可以使用popen或者pipe+fork+execv的手段获取其他进程的输出结果
    • 下载文件
    • 上传文件

下图是实现如上功能后应该得到的输出

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp

总体而言,这个Lab难度不大,和同学交流后大家的主要时间都花在处理字符串上。我认为亲自完成这个Lab会收获很好的入门体验。在写完open和auth功能后就会基本了解、熟练各个操作,剩下的ls、get、put就会变得非常简单

一、知识补充

make/git知识在这里就不补充了,详情可见PKU网络课程实践

socket编程:

这里直接引用助教老师给的例子

首先是Server

sock = socket(AF_INET, SOCK_STREAM, 0); // 申请一个TCP的socket
struct sockaddr_in addr; // 描述监听的地址
addr.sin_port = htons(23233); // 在23233端口监听 htons是host to network (short)的简称,表示进行大小端表示法转换,网络中一般使用大端法
addr.sin_family = AF_INET; // 表示使用AF_INET地址族
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); // 监听127.0.0.1地址,将字符串表示转化为二进制表示
bind(sock, (struct sockaddr*)&addr, sizeof(addr));
listen(sock, 128);
int client = accept(sock, nullptr, nullptr);

其次是Client

sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_port = htons(23233);
addr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); // 表示我们要连接到服务器的127.0.0.1:23233
connect(sock, (struct sockaddr*)&addr, sizeof(addr));

这里就基本完成了服务端和客户端的连接,如果再加上数据报文的发送和接收(send和recv函数),实际上就已经完成了open的功能。其中的几个比较重要的函数可以自行搜索了解,尤其重点要注释好函数需要的参数的意义返回值。(需要引用的头文件可自行查询)

首先给出sockaddr_in的数据结构

struct sockaddr_in 是描述 IPv4 套接字地址的结构
sin_family AF_INET(IPv4) ,编程时实际使用地址类型
sin_port :存放端口号 ( 按照网络字节序存储 )
sin_addr :存放 32 IP 地址 ( 无符号整数
sin_zero:为与 sockaddr 大小兼容而保留的空字节

struct sockaddr可以看作struct sockaddr_in的父类

bind函数用来将服务器本地套接字地址sa与描述符socket_fd绑定,在服务器端调用。其返回值建议进行一层判断,若不为0则可输出“bind error”(在测试时需要换port)

connect函数用于建立连接,客户端调用connect向服务器发起建立连接的请求。

listen函数用于监听。

(这几个函数建议初学者再上网搜索下)

在后续编写时要注意大小端的转换(利用htons等函数),如果编译遇到栈溢出的报错则大概率是这个原因。

在这个例子中端口值默认为23233。

那么server和client如何互相传输呢?助教老师的教程又给出了例子。这个例子实现了Client向Server发送字符串“Hello Server”,而Server收到数据后又会传回Client。

Server:

char buffer[128];
size_t l = recv(client, buffer, 128, 0);
send(client, buffer, l, 0);

Client

char buffer[128];
sprintf(buffer, "Hello Server");
send(sock, buffer, strlen(buffer)+1, 0);
recv(sock, buffer, 128, 0);

这里面最重要的就是send和recv函数,但是使用默认的send和recv并不会一直正确,当遇到大文件传输时,有时并不是所有数据都能成功放入缓冲区,你可能无法像自己想的那样从缓冲区内读取定长的内容,因为建立safe_send和safe_recv是非常重要的。

这里给出safe_send的例子

size_t ret = 0;
while (ret < len) {
    size_t b = send(sock, buffer + ret, len - ret, 0);
    if (b == 0) printf("socket Closed"); // 当连接断开
    if (b < 0) printf("Error ?"); // 这里可能发生了一些意料之外的情况
    ret += b; // 成功将b个byte塞进了缓冲区
}

这段代码很好理解,如果没有发送完自己需要的长度,则while循环会保证多次send直到达成目的。safe_recv同理,除了换recv和对应sock不用 进行额外处理。

二、具体实现

1.数据报文格式和字符串处理

数据报文老师的指导上已经明确给出

struct {
    byte m_protocol[MAGIC_NUMBER_LENGTH]; /* protocol magic number (6 bytes) */
    type m_type;                          /* type (1 byte) */
    status m_status;                      /* status (1 byte) */
    uint32_t m_length;                    /* length (4 bytes) in Big endian*/
} __attribute__ ((packed));

其中  __attribute__ ((packed)) 是为了进行数据结构的对齐

m_protocol为默认的"\xe3myftp"

m_type则用于区分数据报文是属于哪一部分,如client的open请求时发送数据报文的type是0xa1,server回应open的数据报文的type为0xa2。

m_status在一些功能中鉴定是否成功,如client想下载server中的文件,若server可以找到这个文件,status设置为1,client在读到这一数字后才会进行下载操作(实际为通过socket传字符串),反之若为0,client将直接结束这个操作。

m_length在处理payload字符串时非常重要,server和client在接收对方的额外数据时,需要通过length来计算长度,而有了长度才能知道从缓冲区中接收多少内容,length计算不准会导致意想不到的错误。一般情况下length为12。

下面给出我成功实现的数据报文结构体


struct Header
{
    char m_protocol[MAGIC_NUMBER_LENGTH]; /* protocol magic number (6 bytes) */
    uint8_t m_type;                          /* type (1 byte) */
    uint8_t m_status;                      /* status (1 byte) */
    uint32_t m_length;                    /* length (4 bytes) in Big endian*/
} __attribute__ ((packed)); 

另外需要处理命令行的读取,即通过空格来拆解字符串,以达到提取关键信息的目的。如键入“open 127.0.0.1 12323”,则函数处理后得到“open”、“127.0.0.1”、“12323”。我使用getcommand函数来读取字符串,parseline函数来分割字符串并存储,只要达成所需的目的即可。

int getcommand(char *buf)
{
    memset(buf,0,buffsize);
    int length;
    fgets(buf,buffsize,stdin);
    length=strlen(buf);
    buf[length-1]='\0';
    return strlen(buf);
}

void parseline(char* cmd)
{
    int i,j,k;
    int len = strlen(cmd);
    int num=0;
    for(i=0;i<maxargs;i++)
    {
        argv[i] = NULL;
    }

    char tmp[buffsize];
    j=-1;
    for(i=0;i<=len;i++)
    {
        if(cmd[i]==' '||i==len)
        {
            if(i-1>j)
            {
                cmd[i]='\0';
                argv[num++] = cmd+j+1;
            }
            j = i;
        }
    }
    argc = num;
    argv[argc] = NULL;
}

最后还要注意维持状态码的更新,教程中给出了FSM示意图

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp

状态码有三个值即可

int status_flag=0;// 0 unconnected
                  // 1 connect success
                  // 2 auth success

safe_send的代码如下(safe_recv同理)

void safe_send(int sock,Header* buffer, int len,int d)
{
    size_t ret = 0;
    while (ret < len)
    {
        size_t b = send(sock, buffer + ret, len - ret, 0);
        if (b == 0) printf("socket Closed"); // 当连接断开
        if (b < 0) printf("Error ?"); // 这里可能发生了一些意料之外的情况
        ret += b; // 成功将b个byte塞进了缓冲区
    }
}

void safe_send(int sock,char* buffer, int len,int d)
{
    size_t ret = 0;
    while (ret < len)
    {
        size_t b = send(sock, buffer + ret, len - ret, 0);
        if (b == 0) printf("socket Closed"); // 当连接断开
        if (b < 0) printf("Error ?"); // 这里可能发生了一些意料之外的情况
        ret += b; // 成功将b个byte塞进了缓冲区
    }
}

到这里准备工作就结束了,配合上前面的socket编程,open功能应该能被顺利解决 

2.open函数

首先是数据报文的初始化,来看一下要求的数据报文内容

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp

而open功能的发送顺序如下图所示 

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp

open的整个过程只要按照上图完成即可:客户端输入命令行指令,程序通过读取命令行字符串识别出要进行open操作,随后进入open函数,client发送报文—— server接收报文并完成连接——server发送报文——client接收报文。

我们对各自的报文进行初始化(后续功能的初始化函数,包括FILE_DATA的都不再放出,结构大同小异)

Server:(其中recv_header是用来接受Client发送的数据报文)

void  OPEN_CONN_REPLY()
{
    memcpy(recv_header.m_protocol,magic_number,6);
    recv_header.m_type=0xa2;
    recv_header.m_status=1;
    recv_header.m_length=12;
}

Client:

void  OPEN_CONN_REQUEST()
{
    memcpy(cli_header.m_protocol,magic_number,6);
    cli_header.m_type=0xa1;
    cli_header.m_status=0;
    cli_header.m_length=htonl(12);

}

结合前面的parseline函数,在client中进入open的分支,代码如下

void open()
{
    struct sockaddr_in servaddr;
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    bzero(&servaddr,sizeof(servaddr));  
    //初始化地址结构体
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(stoi(argv[2]));
    inet_pton(AF_INET,argv[2],&servaddr.sin_addr);
    //表示我们要连接到服务器
    int ret=connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    if(ret==-1)
    {
        cout<<"Error:connect error_1"<<endl;
    }
    else
    {
        memset(recv_buf,0,buffsize);
        memset(send_buf,0,buffsize);
        OPEN_CONN_REQUEST();
        safe_send(sockfd,&cli_header,header_length,0);
        safe_recv(sockfd,recv_buf,header_length,0);
        recv_header=(struct Header*)recv_buf;
        
        if(str_equal(magic_number, recv_header->m_protocol,MAGIC_NUMBER_LENGTH) 
        && recv_header-> m_type == 0xA2 && recv_header->m_status == 1)
        {
           fprintf(stdout,"Server connection accepted.\n");
           status_flag = 1;         //connect success
        }
        else
        {
            fprintf(stdout,"Error:connect error_2\n");
        }
    }
}

注意要检查Server发来的报文内容是否正确(其中str_equal用于检查m_protocol内容是否是“/xe3myftp”)

Server:

我的Server的结构不是很好看,在main部分进行了状态码的判定,并且根据m_type进入相应功能。实际上可以用函数进行结构简化,看起来会更清晰


int main(int argc, char ** argv) 
{
    
    //listenfd是监听套接字,connfd是与客户端数据通信的套接字
    socklen_t clilen;
    struct sockaddr_in cliaddr,servaddr;
    listenfd= socket(AF_INET,SOCK_STREAM,0);
    bzero(&servaddr,sizeof(servaddr));

    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    servaddr.sin_port=htons(stoi(argv[2]));
    int bind_flag;
    bind_flag=bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    listen(listenfd,LISTENQ);
    if(bind_flag!=0)
    {
        cout<<"Error:bind error"<<endl;
    }
    else
    {
        cout<<"else"<<endl;
         while(1)
        {
            cout<<status_flag<<endl;

            if(status_flag==0)
            {
                //cout<<"open"<<endl;
                clilen=sizeof(cliaddr);
                connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);
                //accept返回值(connfd)是用来和客户端交换数据的临时套接字描述符
                memset(recv_buf,0,buffsize);
                memset(send_buf,0,buffsize);
            
                safe_recv(connfd,recv_buf,header_length,0);
                recv_header2=(struct Header*)recv_buf;
                if(recv_header2->m_type==0xa1)
                {
                    status_flag=1;
                    open();
                    cout<<"open"<<status_flag<<endl;
                }
                else
                {
                    cout<<"no connect"<<endl;
                }
            }
            else 
            {
                //其他功能处理
            }
        }
    }
} 

在main函数的主体中已经接收了一次报文,故server的open函数主要就是发送数据报文。

PS:这里的recv_header实际上是要发送的报文,这个命名很差,但是我懒得改了orz

void open()
{
    OPEN_CONN_REPLY();
    recv_header.m_length=htonl(12);
    safe_send(connfd,&recv_header,header_length,0);
}

到这里就完成了open的实现

3.auth

auth和open的唯一区别在于client需要发送一段额外的字符串,包含着用户名和密码的信息(默认为user和123123)

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp

 实际上就是client发送两次(数据报文和payload),而server接收两次。将收、发分为两次,可以帮助接收方明确payload的大小(数据报文大小恒为12,从数据报文中读出m_length后再减12即使payload的大小)。

Client:

void auth(int argc, char ** argv)
{
    memset(recv_buf,0,buffsize);
    memset(send_buf,0,buffsize);
    memset(payload,0,sizeof(payload));
    AUTH_REQUEST();

    //payload memcopy
    strcpy(payload,argv[1]); 
    payload[strlen(payload)]=' ';   
	strcat(payload, argv[2]);

    int send_length=12+strlen(payload)+1;
    cli_header.m_length=send_length;
    cli_header.m_length=htonl(cli_header.m_length);

    int payload_length=send_length-12;
    payload[payload_length-1]='\0';

    safe_send(sockfd,&cli_header,header_length,0);
    safe_send(sockfd,payload,payload_length,0);
    
    safe_recv(sockfd,recv_buf,header_length,0);
    recv_header=(struct Header*)recv_buf;

    if(str_equal(magic_number, recv_header->m_protocol,MAGIC_NUMBER_LENGTH) 
    && recv_header-> m_type == 0xA4 && recv_header->m_status == 1)
    {
       fprintf(stdout,"Authentication granted.\n");
       status_flag = 2;         //auth success
    }
    else
    {

        fprintf(stdout,"Error: Authentication rejected. Connection closed\n");
        close(sockfd);
        status_flag=0;
    }

}

Server:

注意由于在main中已经接收了数据报文,所以auth函数中仅仅接收payload即可

void auth()
{
    init();
    AUTH_REPLY();

    recv_header.m_length=htonl(12);
    int payload_length=ntohl(recv_header2->m_length)-12;
    safe_recv(connfd,payload_buf,payload_length,0);

    if(str_equal(payload_buf,user,payload_length))
    {
        recv_header.m_status=1;
        safe_send(connfd,&recv_header,header_length,0);
        status_flag=2;
        cout<<"auth"<<status_flag;
        return;
    }
    else
    {
        status_flag=0;
        safe_send(connfd,&recv_header,header_length,0);
    }
}

4.ls

如果已经熟练掌握了包含payload的收发,后面三个功能都可以很快写完。

ls实际上就是利用popen直接得到linux中键入ls的结果,以读文件的方式将该结果传入payload,Client接收Server发来的payload后打印即可。

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp

所以先看Server:

void ls()
{
    init();
    LIST_REPLY();

    FILE *file;
    char line[_LINE_LENGTH];
    file = popen("ls", "r");
    int flag=1;
    if (NULL != file)
    {
        while (fgets(line, _LINE_LENGTH, file) != NULL)
        {
            if(flag)
            {
                strcpy(payload_buf,line);
                flag=0;
            }
            else
            {
                strcat(payload_buf,line);
            }

        }
    }
    pclose(file);

    int payload_length=strlen(payload_buf);
    payload_buf[payload_length]='\0';
    payload_length++;
    recv_header.m_length=htonl(12+payload_length);
    payload_length=htonl(payload_length);
    safe_send(connfd,&recv_header,header_length,0);
    safe_send(connfd,payload_buf,payload_length,0);
}

Client:

(换行符也会被读入payload,故不需要关心换行,Client会直接打印出来)

void ls()
{
    fprintf(stdout,"----- file list start -----\n");
    memset(recv_buf,0,buffsize);
    memset(send_buf,0,buffsize);
    LIST_REQUEST();
    safe_send(sockfd,&cli_header,header_length,0);
    safe_recv(sockfd,recv_buf,header_length,0);
    recv_header=(struct Header*)recv_buf;
    int payload_length=ntohl(recv_header->m_length)-12;

    if(str_equal(magic_number, recv_header->m_protocol,MAGIC_NUMBER_LENGTH) 
    && recv_header-> m_type == 0xA6 )
    {
        safe_recv(sockfd,payload,payload_length,0);
        for(int i=0;i<payload_length;i++)
        {
           cout<<payload[i];
        }
        fprintf(stdout,"----- file list end -----\n");

    }
}

5.get和put

put和get可以互相抄写(put的server和get的client一样,put的client和get的server一样),因此在这里给出get的代码

其实这两个函数没有难点但是有些麻烦,建议在头脑清醒的时候慢慢写,否则遇到bug还是会头疼。

注意读完文件内容存入payload后,不需要在结尾加\0

如果对于字符串处理没有信心,建议将发送报文前和后,分别打印payload长度和payload内容,来看传、收处理是否有问题。

读写文件的部分如果不熟悉可以上网搜索,有很多种实现方法。

数据报文仅仅给出get的,详细内容(包含put的数据报文)依然见PKU网络课程实践

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp

流程如下:

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp

Client:

void get(int argc, char ** argv)
{
    memset(recv_buf,0,buffsize);
    memset(send_buf,0,buffsize);
    GET_REQUEST();
    memcpy(payload,argv[1],strlen(argv[1]));

    //cout<<"argv changdu"<<strlen(argv[1])<<endl;

    //safe_send(sockfd,&cli_header,header_length,0);

    int payload_length=strlen(payload);
    payload[payload_length]='\0';
    payload_length++;

    /*cout<<"test1"<<endl;
    for(int i=0;i<payload_length;i++)
    {
        cout<<payload[i];
    }
    cout<<endl;
    cout<<"test2"<<endl;
    */
   //success

    cli_header.m_length=12+payload_length;
    cli_header.m_length=htonl(cli_header.m_length);

    safe_send(sockfd,&cli_header,header_length,0);
    safe_send(sockfd,payload,payload_length,0);

    safe_recv(sockfd,recv_buf,header_length,0);
    recv_header=(struct Header*)recv_buf;

    //cout<<int(recv_header->m_status)<<endl;
    //success

    if(str_equal(magic_number, recv_header->m_protocol,MAGIC_NUMBER_LENGTH) 
    && recv_header-> m_type == 0xA8 && recv_header->m_status == 1)
    {
        //cout<<1<<endl;
        //success

        memset(recv_buf,0,buffsize);
        safe_recv(sockfd,recv_buf,header_length,0);
        Header* get_header=(struct Header*)recv_buf;

        payload_length=ntohl(get_header->m_length)-12;

        //cout<<"file length "<<payload_length<<endl;
        memset(payload,0,sizeof(payload));
        safe_recv(sockfd,payload,payload_length,0);

        FILE* new_file;
        new_file=fopen(argv[1],"w");
        int j=0;
        if (new_file == NULL)
        {
            fprintf(stdout,"write file error...\n");
        }
        fwrite(payload, sizeof(size_t), payload_length, new_file);
        fclose(new_file);
        /*for(int i=0;i<payload_length;i++)
        {
            cout<<payload[i];
        }
        success
        */

        fprintf(stdout,"File downloaded.\n");
    }
}

 Server:

void get()
{
    //cout<<ntohl(recv_header2->m_length)<<endl;
    //success 12+10

    int payload_length=ntohl(recv_header2->m_length)-12;

    //cout<<"test 1.payload_length 10 ? "<<payload_length<<endl;
    //success

    safe_recv(connfd,payload_buf,payload_length,0);

    //FOPEN
    /*for(int i=0;i<payload_length;i++)
    {
        cout<<payload_buf[i];
    }
    cout<<endl;
    */
   //success chang.txt

    FILE *fp;
    fp=fopen(payload_buf,"r");


    GET_REPLY();
    recv_header.m_length=htonl(12);

    if(fp == NULL)
    {
        fclose(fp);
        safe_send(connfd,&recv_header,header_length,0);
        //cout<<int(recv_header.m_status)<<endl;
        printf("Fail to open file!\n");
        return;
    }
    else
    {
        recv_header.m_status=1;
        safe_send(connfd,&recv_header,header_length,0);

        //printf("%d",recv_header.m_status);  //1
        //success

        memset(payload_buf,sizeof(payload_buf),0);

        unsigned char character = 0;
        int i=0;
        while (!feof(fp)) 
        {
            character = getc(fp);
            payload_buf[i]=character;
            i++;
        }
        i--;
        fclose(fp);
        payload_length=i;
        //cout<<"payload_length ,file_length  ?? "<<payload_length<<endl;
        //success
        /*fseek(fp, 0, SEEK_END); 
        int fileSize;
        fileSize = ftell(fp); 
        cout<<"filesize ?"<<fileSize<<endl;;
        fread(payload_buf,fileSize,sizeof(char),fp);
        payload_buf[fileSize]='\0';
        fclose(fp);
        fileSize++;
         cout<<"filesize ?"<<fileSize<<endl;;
        */

        FILE_DATA();
        recv_header.m_length=htonl(12+payload_length);
        safe_send(connfd,&recv_header,header_length,0); //FILE_DATA SEND

        //printf("%d",payload_length);   
        //cout<<endl;
        //printf("%d",fileSize);

        /*for(int i=0;i<payload_length;i++)
        {
            cout<<payload_buf[i];
        } 
        success
        */

        safe_send(connfd,payload_buf,payload_length,0);
        return;
    }
}

(quit功能省略)

目前得分

北京大学计算机网络lab1——MyFTP,计算机网络,网络,websocket,tcp


三、总结

lab1的整体难度并不高,但是最后早点开始,平和的心态可以更快的完成,如果着急完成反而会因为细节处理的不好出bug(大佬忽略)

我的整体代码可能因为状态机的问题实际上仍然存在问题,但是由于test并不会检测这一部分,所以基础的90分可以拿到。

总之这个lab可以让人快速了解socket和ftp的工作原理,也为后面的lab打下了基础,所以一定要自己完成。文章来源地址https://www.toymoban.com/news/detail-732030.html

到了这里,关于北京大学计算机网络lab1——MyFTP的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【网络安全】【密码学】【北京航空航天大学】实验三、数论基础(下)【C语言实现】

    1、中国剩余定理(Chinese Remainder Theorem) (1)、算法原理 m1 , m2 , … mk 是一组 两两互素 的正整数,且 M = m1 · m2 · … · mk 为它们的乘积, 则如下的同余方程组: x == a1 (mod m1) x == a2 (mod m2) … x == ak (mod mk) 对于 模M 有唯一的解 x = (M · e1 · a1 / m1 + M · e2 · a2 / m2 + … + M · ek ·

    2024年02月02日
    浏览(51)
  • 山东大学计算机网络期末

    内容仅供参考。如有错误之处,敬请指正! 第一章 概述 第二章 物理层 第三章 数据链路层 第四章 介质访问子层 第五章 网络层 第六章 传输层 第七章 应用层 1.基本概念 计算机网络定义: 表示一组通过单一技术相互连接起来的自主计算机集合。 分布式系统: 是建立在网络

    2024年02月03日
    浏览(58)
  • 【网络安全】【密码学】【北京航空航天大学】实验一、数论基础(上)【C语言和Java实现】

    1、通过本次实验,熟悉相关的编程环境,为后续的实验做好铺垫; 2、回顾数论学科中的重要基本算法,并加深对其的理解,为本学期密码学理论及实验课程打下良好的基础。 数论主要研究的是整数的运算及性质,许多常用的加密算法都用到了数论知识。 本次实验的实验环

    2024年01月25日
    浏览(61)
  • 【网络安全】【密码学】【北京航空航天大学】实验二、数论基础(中)【C语言和Java实现】

    1、扩展欧几里得算法(Extended Euclid’s Algorithm) (1)、算法原理 已知整数 a , b ,扩展的欧几里得算法可以在求得 a , b 的 最大公约数 的同时,找到一对整数 x , y ,使得 a , b , x , y 满足如下等式: ax + by = d = gcd(a,b) , 其中 gcd(a, b) 为 a 和 b 的最大公约数。 (2)、算法流程 本算

    2024年02月01日
    浏览(60)
  • 济南大学 《计算机网络2a》期末考点

    Part 2  交换 交换机的学习和转发方法 学习:源地址构建MAC地址表 转发:包括两种方法——存储转发交换、直通交换 注意:交换机永远不会把流量从它接收到的接口转发出去 冲突域广播域 交换机和路由器划分冲突域 路由器划分广播域 Part 3  VLAN 基本知识点 默认所有端口属

    2024年01月17日
    浏览(60)
  • 合肥工业大学计算机网络实验一

    计算机网络实验报告# ✅作者简介:CSDN内容合伙人、信息安全专业在校大学生🏆 🔥系列专栏 :hfut实验报告 📃新人博主 :欢迎点赞收藏关注,会回访! 💬舞台再大,你不上台,永远是个观众。平台再好,你不参与,永远是局外人。能力再大,你不行动,只能看别人成功!

    2024年02月03日
    浏览(54)
  • 计算机网络+线性代数+大学物理

    不加湘潭大学的tag,防止曝光率太高哈哈 选择题确定的是5个题,填空题确定的是2个题,简答题前两个确定,然后就没有了,2×7+6×2,26+,确实是比较难,我复习的方向和考试的方向偏差比较大 等成绩出来了我在评论区更新我的分数,下学期开始不能再考前速成了,一方

    2024年02月02日
    浏览(45)
  • 【北京航空航天大学】【信息网络安全实验】【实验一、密码学:DES+RSA+MD5编程实验】

    1. 通过对DES算法的代码编写,了解分组密码算法的设计思想和分组密码算法工作模式; 2. 掌握RSA算法的基本原理以及素数判定中的Rabin-Miller测试原理、Montgomery快速模乘(模幂)算法,了解公钥加密体制的优缺点及其常见应用方式; 3. 掌握MD5算法的基本原理,了解其主要应用

    2024年02月19日
    浏览(52)
  • 吉林大学 计算机网络常见的名词解释

    API (Application Programming Interface)应用程序编程接口 HTTP (Hyper Text Transfer Protocol) 超文本传输协议 CDN (Content Delivery Network)内容分发网络 SMTP (Simple Mail Transfer Protocol)即简单邮件传输协议 POP3 (Post Office Protocol),即第三版的邮局协议,用于电子邮件的接收。本协议主要用

    2024年02月05日
    浏览(50)
  • 山东大学软件学院计算机网络期末考试考点

    单播 :只有一个发送方和一个接收方的点到点传输。 组播 :将一个数据包发送给一组机器,即所有机器的一个子集。 广播 :将一个数据包发送给所有的目标机器。 面向连接的服务 :按照电话系统建模,服务用户首先必须建立一个连接,然后使用该连接传输数据,最后释放连接。

    2024年02月03日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包