热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

利用原始socket简单实现FTP的客户端和服务器端程序

1.设计目的本设计旨在利用原始socket简单实现FTP(FileTransferProtocol,文件传输协议)的客户端和服务器端程序,能够实现get、put、pwd、dir、cd等基

1.设计目的
本设计旨在利用原始socket简单实现FTP(File Transfer Protocol,文件传输协议)的客户端和服务器端程序,能够实现get、put、pwd、dir、cd等基本交互命令。

2.具体要求
用socket 编程接口编写两个程序,分别为客户程序(client.c)和服务器程序(server.c),该程序应能实现下述命令功能:
get:取远方的一个文件
put:传给远方一个文件
pwd:显示远主当前目录
dir:列出远方当前目录
cd :改变远方当前目录
? :显示你提供的命令
quit :退出返回

3.程序实现
3.1 client.c(客户端代码实现)

#include "Winsock.h"
#include "windows.h" 
#include "stdio.h" 
#include "time.h" 
#include 
using namespace std;
#define RECV_PORT 3312 
#define SEND_PORT 4302 
#pragma comment(lib, "wsock32.lib")
SOCKET sockclient;
char filename[20];                       //文件名 
sockaddr_in ServerAddr;                 //服务器地址 
char rbuff[1024];                        //接收缓冲区 
char sbuff[1024];                       //发送缓冲区 
char InputIP[20];                       //存储输入的服务器IP


void help()//处理help
{
 cout <<" 欢迎进入迷你FTP帮助菜单 " <" * * * * * * * * * * * * * * * * * * * * * " <" 1.get....................下载(接受)文件 " <" get的用法: get 文件名 " <" 2.put.................上传(发送)文件 " <" put的用法:put 文件名 " <" 3.pwd..........显示当前文件夹的绝对路径 " <" 4.dir............显示远方当前目录的文件 " <" 5.cd.............改变远方当前目录和路径 " <" cd的用法(进入下级目录): cd 路径名 " <" cd的用法(进入上级目录): cd .. " <" 6.?或者help................进入帮助菜单 " <" 7.quit..........................退出FTP " <" * * * * * * * * * * * * * * * * * * * * * " <void list(SOCKET sockfd)
{
    int nRead;
    while (true)
    {
        nRead = recv(sockclient, rbuff, 1024, 0);
        //recv函数通过sockclient套接口接受数据存入rbuff缓冲区,返回接受到的字节数 
        if (nRead == SOCKET_ERROR)
        {
            printf("read response error!\n");
            exit(1);
        }
        if (nRead == 0)//数据读取结束 
            break;
        //显示数据 
        rbuff[nRead] = '\0';
        printf("%s", rbuff);
    }
}

/*********************** put:传给远方一个文件***************************/
int SendFile(SOCKET datatcps, FILE* file)
{
    printf(" sending file data..");
    for (;;)  //从文件中循环读取数据并发送客户端 
    {
        int r = fread(sbuff, 1, 1024, file);//fread函数从file文件读取1个1024长度的数据到sbuff,返回成功读取的元素个数 
        if (send(datatcps, sbuff, r, 0) == SOCKET_ERROR)
        {
            printf("lost the connection to client!\n");
            closesocket(datatcps);
            return 0;
        }
        if (r<1024)                      //文件传送结束 
            break;
    }
    closesocket(datatcps);
    printf("done\n");
    return 1;
}


DWORD StartSock()//启动winsock 
{
    WSADATA WSAData;
    char a[20];
    memset(a, 0, 20);
    if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)//加载winsock版本
    {
        printf("sock init fail!\n");
        return (-1);
    }
    if (strncmp(InputIP, a, 20) == 0)
    {
        printf("请输入连接的主机IP:");
        scanf("%s", &InputIP);
    }
    //设置地址结构
    ServerAddr.sin_family = AF_INET;//AF_INET表示使用IP地址族 
    ServerAddr.sin_addr.s_addr = inet_addr(InputIP);//指定服务器IP 
    ServerAddr.sin_port = htons(RECV_PORT);//设置端口号 
    return(1);
}


//创建套接字 
DWORD CreateSocket()
{
    sockclient = socket(AF_INET, SOCK_STREAM, 0);//当socket函数成功调用时返回一个新的SOCKET(Socket Descriptor) 
    if (sockclient == SOCKET_ERROR)
    {
        printf("sockclient create fail! \n");
        WSACleanup();
        return(-1);
    }
    return(1);
}
DWORD CallServer() //发送连接请求 
{
    CreateSocket();

    if (connect(sockclient, (struct  sockaddr*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)  
    {//connect函数创建与指定外部端口的连接
        printf("Connect fail \n");
        memset(InputIP, 0, 20);
        return(-1);
    }
    return(1);
}

DWORD TCPSend(char data[])   //发送命令 
{
    int length;
    length = send(sockclient, data, strlen(data), 0);
    //send函数通过sockclient接口发送data里面的数据,发送成功返回发送的字节数 
    if (length <= 0)
    {
        printf("send data error ! \n");
        closesocket(sockclient);
        WSACleanup();
        return(-1);
    }
    return(1);
}


int main()
{
    char messge1[10];           //定义输入要处理的文件名 
    char messge2[20];           //定义输入要处理的文件名 
    char order[30];             //输入的命令 
    order[0] = '\0';
    char buff[80];              //用以存储经过字串格式化的order 
    FILE *fd;                   //File协议主要用于访问本地计算机中的文件,fd指针指向要访问的目标文件 
    FILE *fd2;
    int count;
    int sin_size = sizeof(ServerAddr);
    StartSock();
    if (CallServer() == -1)
        return main();          //发送连接请求失败,返回主函数 
    printf("\n请输入命令(输入?或help进入帮助菜单):\n");
    memset(buff, 0, 80);            //清空数组 
    memset(messge2, 0, 20);
    memset(order, 0, 30);
    memset(messge1, 0, 10);
    memset(rbuff, 0, 1024);
    memset(sbuff, 0, 1024);
    scanf("%s", &messge1);//s%输入字符串
    if (strncmp(messge1, "get", 3) == 0)
        scanf("%s", &messge2);
    if (strncmp(messge1, "put", 3) == 0)
        scanf("%s", &messge2);
    if (strncmp(messge1, "cd", 2) == 0)
        scanf("%s", &messge2);
    strcat(order, messge1);         //把messge1加在order的末尾 
    strcat(order, " ");             //命令中间的空格 
    strcat(order, messge2);         //把messge2加在order的末尾 
    sprintf(buff, order);           //把调整格式的order存入buff

    //help和? 
    if (strncmp(messge1, "help", 4) == 0) {
        help();
    }
    if (strncmp(messge1, "?", 1) == 0){
        help();
    }

    if (strncmp(messge1, "quit", 4) == 0)
    {
        printf(" 欢迎再次进入迷你FTP,谢谢使用!\n");
        closesocket(sockclient);
        WSACleanup();
        return 0;
    }
    TCPSend(buff);//发送buff里面的数据 
    recv(sockclient, rbuff, 1024, 0);
    printf(rbuff);

    if (strncmp(rbuff, "get", 3) == 0)      //get
    {
        fd = fopen(messge2, "wb");//使用二进制方式,打开文件,wb只写打开或新 建一个二进制文件;只允许写数据。 
        if (fd == NULL)
        {
            printf("open file %s for weite failed!\n", messge2);
            return 0;
        }
        while ((count = recv(sockclient, rbuff, 1024, 0))>0)
        {
            fwrite(rbuff, sizeof(rbuff), count, fd);
        }
        //把count个数据长度为size0f()的数据从 rbuff输入到fd指向的目标文件 
        fclose(fd);        //关闭文件 
    }

    if (strncmp(rbuff, "put", 3) == 0)   //put 
    {
        strcpy(filename, rbuff + 9);
        fd2 = fopen(filename, "rb");//rb读写打开一个二进制文件,只允许读写数据。 
        if (fd2)
        {
            if (!SendFile(sockclient, fd2)){
                printf("send failed!");
                return 0;
            }
            fclose(fd2);
        }//关闭文件 

        else//打开文件失败 
        {
            strcpy(sbuff, "can't open file!\n");
            if (send(sockclient, sbuff, 1024, 0))
                return 0;
        }
    }

    if (strncmp(rbuff, "dir", 3) == 0)      //dir 
    {
        printf("\n");
        list(sockclient);               //列出接受到的列表内容
    }
    if (strncmp(rbuff, "pwd", 3) == 0)
    {
        list(sockclient);               //列出接受到的内容--绝对路径
    }
    if (strncmp(rbuff, "cd", 2) == 0){}      //cd 

    closesocket(sockclient);            //关闭连接
    WSACleanup();                       //释放Winsock 
    return main();
}

3.2 server.c(服务端代码实现)

#include "Winsock.h" 
#include "windows.h" 
#include "stdio.h" 
#define RECV_PORT 3312 
#define SEND_PORT 4302 
#pragma comment(lib, "wsock32.lib")
SOCKET sockclient, sockserver;
 struct sockaddr_in ServerAddr;//服务器地址
 struct sockaddr_in ClientAddr;//客户端地址 

/***********************全局变量***********************/
int Addrlen;//地址长度 
char filename[20];//文件名 
char order[10];//命令 
char rbuff[1024];//接收缓冲区 
char sbuff[1024];//发送缓冲区 


DWORD StartSock()    //初始化winsock 
{
    WSADATA WSAData;
    if (WSAStartup(MAKEWORD(2, 2), &WSAData) != 0)
    {
        printf("socket init fail!\n");
        return (-1);
    }
    return(1);
}

DWORD CreateSocket()
{
    sockclient = socket(AF_INET, SOCK_STREAM, 0);
    if (sockclient == SOCKET_ERROR)
    {
        printf("sockclient create fail ! \n");
        WSACleanup();
        return(-1);
    }
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    ServerAddr.sin_port = htons(RECV_PORT);
    if (bind(sockclient, (struct  sockaddr  FAR  *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
    {                     //bind函数将套接字和地址结构绑定 
        printf("bind is the error");
        return(-1);
    }
    return (1);
}

int SendFileRecord(SOCKET datatcps, WIN32_FIND_DATA *pfd)     //用来发送当前文件记录 
{
    char filerecord[MAX_PATH + 32];
    FILETIME ft;         //文件建立时间 
    FileTimeToLocalFileTime(&pfd->ftLastWriteTime, &ft);
    SYSTEMTIME lastwtime;     //SYSTEMTIME系统时间数据结构 
    FileTimeToSystemTime(&ft, &lastwtime);
    char *dir = pfd->dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY ? "" : " ";
    sprintf(filerecord, "%04d-%02d-%02d %02d:%02d %5s %10d %-20s\n",
        lastwtime.wYear,
        lastwtime.wMonth,
        lastwtime.wDay,
        lastwtime.wHour,
        lastwtime.wMinute,
        dir,
        pfd->nFileSizeLow,
        pfd->cFileName);
    if (send(datatcps, filerecord, strlen(filerecord), 0) == SOCKET_ERROR)
    { //通过datatcps接口发送filerecord数据,成功返回发送的字节数 
        printf("Error occurs when sending file list!\n");
        return 0;
    }
    return 1;
}

int SendFileList(SOCKET datatcps)
{
    HANDLE hff;//建立一个线程 
    WIN32_FIND_DATA fd;   //搜索文件 
//可以通过FindFirstFile()函数根据当前的文件存放路径查找该文件来把待操作文件的相关属性读取到WIN32_FIND_DATA结构中去 
    if (hff == INVALID_HANDLE_VALUE)//发生错误 
    {
        const char *errstr = "can't list files!\n";
        printf("list file error!\n");
        if (send(datatcps, errstr, strlen(errstr), 0) == SOCKET_ERROR)
        {
            printf("error occurs when senging file list!\n");
        }
        closesocket(datatcps);
        return 0;
    }

    BOOL fMoreFiles = TRUE;
    while (fMoreFiles)
    {//发送此项文件信息 
        if (!SendFileRecord(datatcps, &fd))
        {
            closesocket(datatcps);
            return 0;
        }
        //搜索下一个文件 
        fMoreFiles = FindNextFile(hff, &fd);
    }
    closesocket(datatcps);
    return 1;
}

int SendFile(SOCKET datatcps, FILE* file)
{
    printf(" sending file data..");
    for (;;)   //从文件中循环读取数据并发送客户端 
    {
        int r = fread(sbuff, 1, 1024, file);//把file里面的内容读到sbuff缓冲区 
        if (send(datatcps, sbuff, r, 0) == SOCKET_ERROR)
        {
            printf("lost the connection to client!\n");
            closesocket(datatcps);
            return 0;
        }
        if (r<1024)//文件传送结束 
            break;
    }
    closesocket(datatcps);
    printf("done\n");
    return 1;
}

//连接 
DWORD ConnectProcess()
{
    Addrlen = sizeof(ClientAddr);
    if (listen(sockclient, 5)<0)
    {
        printf("Listen error");
        return(-1);
    }
    printf("服务器监听中...\n");
    for (;;)
    {
        sockserver = accept(sockclient, (struct sockaddr FAR *)&ClientAddr, &Addrlen);
        //accept函数取出连接队列的第一个连接请求,sockclient是处于监听的套接字ClientAddr 是监听的对象地址, 
        //Addrlen是对象地址的长度 
        for (;;)
        {
            memset(rbuff, 0, 1024);
            memset(sbuff, 0, 1024);
            if (recv(sockserver, rbuff, 1024, 0) <= 0)
            {
                break;
            }
            printf("\n");
            printf("获取并执行的命令为:");
            printf(rbuff);
            if (strncmp(rbuff, "get", 3) == 0)
            {
                strcpy(filename, rbuff + 4); printf(filename);
                FILE *file; //定义一个文件访问指针 
                //处理下载文件请求 
                file = fopen(filename, "rb");//打开下载的文件,只允许读写 
                if (file)
                {
                    sprintf(sbuff, "get file %s\n", filename);
                    if (!send(sockserver, sbuff, 1024, 0))
                    {
                        fclose(file);      return 0;
                    }
                    else
                    {//创建额外数据连接传送数据 
                        if (!SendFile(sockserver, file))
                            return 0;
                        fclose(file);
                    }
                }//file 

                else//打开文件失败 
                {
                    strcpy(sbuff, "can't open file!\n");
                    if (send(sockserver, sbuff, 1024, 0))
                        return 0;
                } //lost 
            }//get

            if (strncmp(rbuff, "put", 3) == 0)
            {
                FILE *fd;
                int count;
                strcpy(filename, rbuff + 4);
                fd = fopen(filename, "wb");
                if (fd == NULL)
                {
                    printf("open file %s for weite failed!\n", filename);
                    return 0;
                }
                sprintf(sbuff, "put file %s", filename);
                if (!send(sockserver, sbuff, 1024, 0))
                {
                    fclose(fd);
                    return 0;
                }
                while ((count = recv(sockserver, rbuff, 1024, 0))>0)//recv函数返回接受的字节数赋给count 
                    fwrite(rbuff, sizeof(char), count, fd);
                //把count个数据长度为size0f()的数据从rbuff输入到fd指向的目标文件
                printf(" get %s succed!\n", filename);
                fclose(fd);
            }//put 

            if (strncmp(rbuff, "pwd", 3) == 0){
                char   path[1000];
                GetCurrentDirectory(1000, path);//找到当前进程的当前目录 
                strcpy(sbuff, path);
                send(sockserver, sbuff, 1024, 0);
            }//pwd 

            if (strncmp(rbuff, "dir", 3) == 0){
                strcpy(sbuff, rbuff);
                send(sockserver, sbuff, 1024, 0);
                SendFileList(sockserver);//发送当前列表 
            }//dir 

            if (strncmp(rbuff, "cd", 2) == 0)
            {
                strcpy(filename, rbuff + 3);
                strcpy(sbuff, rbuff);
                send(sockserver, sbuff, 1024, 0);
                SetCurrentDirectory(filename);//设置当前目录 
            }//cd 
            closesocket(sockserver);
        }//for 2
    }//for 1 
}


int main()
{
    if (StartSock() == -1)
        return(-1);
    if (CreateSocket() == -1)
        return(-1);
    if (ConnectProcess() == -1)
        return(-1);
    return(1);
}

4.简单的演示效果如下图:
先输入自己电脑的IP地址,然后回车就可以进入到主界面了

这里写图片描述

这里写图片描述


推荐阅读
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 我在这里读了一些答案,说当您的mysql服务器无法在MAMP中启动时,您应该运行sudokillall-9mysqld我过去曾经使用过它,但它一直有效,但是由于某种原因,这次它不起 ... [详细]
  • 【Linux系统编程:基础IO 下】dup2 实现输出重定向、输入重定向、追加重定向 | 理解磁盘 | 理解文件系统中inode的概念 | 软硬链接
    写在前面这里先接着《基础IO上》中的缓冲区的内容作些补充,这里主要补充dup2接口。✔测试用例一:#include#inclu ... [详细]
  • 六十五、主动模式和被动模式、添加监控主机、添加自定义模板、处理图形中的乱码、自动发现...
    六十五、主动模式和被动模式、添加监控主机、添加自定义模板、处理图形中的乱码、自动发现一、主动模式和被动模式主动或者被动是相对客户端来讲的被动模式,服务端会主动连接客户 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • Linux数据链路层的包解析仅以此文作为学习笔记,初学者,如有错误欢迎批评指正,但求轻喷。一般而言,Linux系统截获数据包后,会通过协议栈,按照TCPIP层次进行解析,那我们如何 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Skywalking系列博客1安装单机版 Skywalking的快速安装方法
    本文介绍了如何快速安装单机版的Skywalking,包括下载、环境需求和端口检查等步骤。同时提供了百度盘下载地址和查询端口是否被占用的命令。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
author-avatar
观海望天
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有