热门标签 | 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地址,然后回车就可以进入到主界面了

这里写图片描述

这里写图片描述


推荐阅读
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文详细介绍了 GWT 中 PopupPanel 类的 onKeyDownPreview 方法,提供了多个代码示例及应用场景,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文详细记录了在基于Debian的Deepin 20操作系统上安装MySQL 5.7的具体步骤,包括软件包的选择、依赖项的处理及远程访问权限的配置。 ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 主要用了2个类来实现的,话不多说,直接看运行结果,然后在奉上源代码1.Index.javaimportjava.awt.Color;im ... [详细]
  • 本文深入探讨 MyBatis 中动态 SQL 的使用方法,包括 if/where、trim 自定义字符串截取规则、choose 分支选择、封装查询和修改条件的 where/set 标签、批量处理的 foreach 标签以及内置参数和 bind 的用法。 ... [详细]
  • 本文详细介绍了Java中org.eclipse.ui.forms.widgets.ExpandableComposite类的addExpansionListener()方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。这些示例来源于多个知名开源项目,具有很高的参考价值。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • CMake跨平台开发实践
    本文介绍如何使用CMake支持不同平台的代码编译。通过一个简单的示例,我们将展示如何编写CMakeLists.txt以适应Linux和Windows平台,并实现跨平台的函数调用。 ... [详细]
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社区 版权所有