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

这里写图片描述

这里写图片描述


推荐阅读
  • Web动态服务器Python基本实现
    Web动态服务器Python基本实现 ... [详细]
  • 本文详细介绍了Oracle 11g中的创建表空间的方法,以及如何设置客户端和服务端的基本配置,包括用户管理、环境变量配置等。 ... [详细]
  • 本文详细介绍了如何在Oracle VM VirtualBox中实现主机与虚拟机之间的数据交换,包括安装Guest Additions增强功能,以及如何利用这些功能进行文件传输、屏幕调整等操作。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • 二维码的实现与应用
    本文介绍了二维码的基本概念、分类及其优缺点,并详细描述了如何使用Java编程语言结合第三方库(如ZXing和qrcode.jar)来实现二维码的生成与解析。 ... [详细]
  • 问题场景用Java进行web开发过程当中,当遇到很多很多个字段的实体时,最苦恼的莫过于编辑字段的查看和修改界面,发现2个页面存在很多重复信息,能不能写一遍?有没有轮子用都不如自己造。解决方式笔者根据自 ... [详细]
  • CentOS下ProFTPD的安装与配置指南
    本文详细介绍在CentOS操作系统上安装和配置ProFTPD服务的方法,包括基本配置、安全设置及高级功能的启用。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 本文详细介绍了在Linux操作系统上安装和部署MySQL数据库的过程,包括必要的环境准备、安装步骤、配置优化及安全设置等内容。 ... [详细]
  • 本文档介绍了如何使用ESP32开发板在STA模式下实现与TCP服务器的通信,包括环境搭建、代码解析及实验步骤。 ... [详细]
  • Requests库的基本使用方法
    本文介绍了Python中Requests库的基础用法,包括如何安装、GET和POST请求的实现、如何处理Cookies和Headers,以及如何解析JSON响应。相比urllib库,Requests库提供了更为简洁高效的接口来处理HTTP请求。 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 本文记录了在Windows 8.1系统环境下,使用IIS 8.5和Visual Studio 2013部署Orchard 1.7.2过程中遇到的问题及解决方案,包括503服务不可用错误和web.config配置错误。 ... [详细]
  • 本文详细介绍了如何在Apache Kafka中进行Topic级别的配置,包括创建、修改和删除配置参数的具体步骤,并提供了详细的配置属性表。 ... [详细]
  • C# 中创建和执行存储过程的方法
    本文详细介绍了如何使用 C# 创建和调用 SQL Server 存储过程,包括连接数据库、定义命令类型、设置参数等步骤。 ... [详细]
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社区 版权所有