FTP 编写 3:同时为多个客户端服务(多线程)

多线程FTP(1)——多线程网络通信简单实现 多线程FTP(1)——多线程网络通信简单实现 之前写了一个FTP项目,但实际上我们写的FTP项目只满足单线程——这就像是打电话一样,当一个客户端和服务端连接,其他客户端就会因为服务端占线而无法连接,必须等之前连接的客户端“挂断”才能连接通信。 就像演示的一样, 当一个客户端完成连接(比如说登录,和查看当前目录时) 另一个客户端无法完成和服务端的通信,比如说登录; 而只有在前一个客户端断开连接,服务端才会去处理后一个客户端的信息(即完成登录) 很显然,一次只满足一个用户登录并不满足我们需求,我们想要的肯 阅读详情

FTP 编写 3:同时为多个客户端服务(多线程)


    在上篇文章中写了一个能连接的FTP,但是它只能为一个客户端进行服务,而我们知道在现实生活中我们见到的大部分网络应用都能同时为多个用户提供服务,所以接下来我们的目标是使用多线程来使服务端能同时为多个客户端进行服务。

    C++ 多线程这个不是网络编程的内容,所以在这里不会详细讲解(其实我也不懂啊,刚看的书就来瞎写试试了),并且在这里使用的也不会太复杂(应该)。如果各位有对多线程有疑惑或者有兴趣,如果是计算机专业的并且学过操作系统的可以去看这本关于 C++ 多线程的书:《C++ Concurrency in Action》,这本书从浅入深地进行讲解,很详细,有了操作系统关于线程的知识就容易看懂,上手也比较容易,我就是看了这本书才有胆子试试水的。我也不知道如果没有看过操作系统看这个容易不,你们也可以去试一试,如果感觉困难的话看一看操作系统的线程部分,然后再看。

    好了,也就不废话了,下面开始编写

服务端

    首先对服务端进行编写,在上一篇的代码中进行修改添加。添加的部分是多线程部分。

    首先我们需要把多线程部分和 vector 容器的头文件添加进来:

#include <thread>
#include <vector>

    添加相应的变量,考虑到每有一个客户端进行连接我们就需要新开一个线程专门对其进行服务,是动态变化的,所以我们使用指针,并且将线程在后台进行运行,这样我们就不用再对其进行管理,它自个按照代码运行就行了。而考虑到指针的内存释放问题,我们使用 vector 容器将每个新建的线程指针进行保存,在程序结束的时候释放到其占用的内存,相应的代码如下:

//定义多线程指针,用于创建线程
std::thread* t;
//用于线程的管理,保存创建的多线程指针,程序结束时释放占用的内存
std::vector<std::thread*> tManage;

//创建新的线程,并加入容器中,并将线程后台运行
t = new std::thread(server, sAccept);//server是线程运行函数,sAccept是已经与客户端连接的套接字
tManage.push_back(t);
t->detach();

    接下来是线程运行函数,就是上面线程创建中的 server:其主要的内容是在第一次连接时候发送连接的信息给客户端,就想上一篇文章中的那样,然后我们用一个无限循环,在循环里面接收客户端发送来的信息并打印出来,具体代码如下:

//多线程函数,创建的多线程运行此函数
void server(SOCKET s) {
    SOCKET socket = s;
    struct sockaddr_in ser, cli;//网络地址
    int iSend, iRecv;
    char buf[1024] = "I am a server";

    //显示客户端的 IP 信息
    char clibuf[20] = { '\0' };
    inet_ntop(AF_INET, (void*)&cli.sin_addr, clibuf, 16);
    std::cout << "Accept client IP:" << clibuf << ":" << ntohs(cli.sin_port) << std::endl;

    //发送信息给客户端
    iSend = send(socket, buf, sizeof(buf), 0);
    if (iSend == SOCKET_ERROR) {
        std::cout << "send() Failed\n";
    }
    else if (iSend == 0) {
        std::cout << "send() Zero\n";
    }
    else {
        std::cout << "Send byte:" << iSend << std::endl;
        std::cout << "----------------------------------\n";
    }

    //使用循环不断接受客户端发送来的信息并显示
    while (true) {
        iRecv = recv(socket, buf, sizeof(buf), 0);
        if (iRecv == 0) {
            //std::cout << "recv() Zero\n";
        }
        else if (iRecv == SOCKET_ERROR) {
            std::cout << "recv() Failed\n";
        }
        else {
            std::cout << "recv() data from server:" << buf << std::endl;
        }
    }
}

客户端

    客户端改动的相应较少,也不需要多线程。在上一篇的程序中客户端接收完服务端发送来的信息以后就关闭了套接字,断开了连接,结束了程序。而我们要测试一下我们的多线程是否有用就不能让它接收完消息后就退出了,所以在接收消息的后面我们加了一个循环让其不断的发送消息:我们定义一个数字,这个数字在循环中不断的递增并发送给服务端。而但我们在不同的时间启动多个客户端程序时,这个数字是不一样的,如果在服务端显示正确,那么我们的多线程就是可以使用的。代码如下:

    //对服务端发送指令或者数据
    int index = 0;//此数不断递增,然后发送给服务端,用于验证多线程是否有用
    while (true) {
        //输入相应的数据发送给服务端
        //std::cout << ">>";
        //std::cin >> buf;

        //将数字转换为字符串
        _itoa_s(index, buf, 10);
        ++index;

        //发送数据到服务端
        iSend = send(sClient, buf, sizeof(buf), 0);
        if (iSend == SOCKET_ERROR) {
            std::cout << "send() Failed\n";
        }
        else if (iSend == 0) {
            std::cout << "send() Zero\n";
        }
        else {
            std::cout << "Send byte:" << iSend << std::endl;
            std::cout << "----------------------------------\n";
        }
    }

完整代码如下:

服务端:
#include <Winsock2.h>
#include <Ws2tcpip.h>
#include <iostream>
#include <thread>
#include <vector>
#pragma comment(lib,"ws2_32.lib")

//多线程函数,创建的多线程运行此函数
void server(SOCKET s);

void main()
{
    //定义相关的数据
    int iPort = 5050;//定义其端口
    WSADATA wsaData;//Winsock 的启动参数
    SOCKET sListen, sAccept;//套接口关键字,分别用于监听和接收连接
    int iLen;
    int iSend;
    char buf[] = "I am a server";
    struct sockaddr_in ser, cli;//网络地址
    //定义多线程指针,用于创建线程
    std::thread* t;
    //用于线程的管理,保存创建的多线程指针,程序结束时释放占用的内存
    std::vector<std::thread*> tManage;


    std::cout << "----------------------------\n";
    std::cout << "Server waitting\n";
    std::cout << "----------------------------\n";

    //启动winSocket
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Failed to load Winsock.\n";
        return;
    }

    //创建Socket
    sListen = socket(AF_INET, SOCK_STREAM, 0);
    if (sListen == INVALID_SOCKET) {
        std::cout << "socket() Failed:" << WSAGetLastError() <<"\n";
        return;
    }

    //绑定IP地址
    ser.sin_family = AF_INET;
    ser.sin_port = htons(iPort);
    ser.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(sListen, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR) {
        std::cout << "bind() Failed\n";
        return;
    }

    //监听
    if (listen(sListen, 5) == SOCKET_ERROR) {
        std::cout << "listen() Failed\n";
        return;
    }

    iLen = sizeof(cli);//获取客户端网络地址的长度

    //接受连接和发送欢迎信息
    //用循环使程序一直运行
    while (true) {
        //接收连接
        sAccept = accept(sListen, (struct sockaddr*)&cli, &iLen);
        if (sAccept == INVALID_SOCKET) {
            std::cout << "accept() Failed\n";
            break;
        }

        //创建新的线程,并加入容器中,并将线程后台运行
        t = new std::thread(server, sAccept);
        tManage.push_back(t);
        t->detach();
    }

    //释放指针占用的内存
    for (int i = 0; i < tManage.size(); i++) {
        delete(tManage[i]);
    }

    //关闭监听
    closesocket(sListen);
    //关闭 Winsock
    WSACleanup();
}

//多线程函数,创建的多线程运行此函数
void server(SOCKET s) {
    SOCKET socket = s;
    struct sockaddr_in ser, cli;//网络地址
    int iSend, iRecv;
    char buf[1024] = "I am a server";

    //显示客户端的 IP 信息
    char clibuf[20] = { '\0' };
    inet_ntop(AF_INET, (void*)&cli.sin_addr, clibuf, 16);
    std::cout << "Accept client IP:" << clibuf << ":" << ntohs(cli.sin_port) << std::endl;

    //发送信息给客户端
    iSend = send(socket, buf, sizeof(buf), 0);
    if (iSend == SOCKET_ERROR) {
        std::cout << "send() Failed\n";
    }
    else if (iSend == 0) {
        std::cout << "send() Zero\n";
    }
    else {
        std::cout << "Send byte:" << iSend << std::endl;
        std::cout << "----------------------------------\n";
    }

    //使用循环不断接受客户端发送来的信息并显示
    while (true) {
        iRecv = recv(socket, buf, sizeof(buf), 0);
        if (iRecv == 0) {
            //std::cout << "recv() Zero\n";
        }
        else if (iRecv == SOCKET_ERROR) {
            std::cout << "recv() Failed\n";
        }
        else {
            std::cout << "recv() data from server:" << buf << std::endl;
        }
    }
}
客户端
#include <Winsock2.h>
#include <WS2tcpip.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")

void main()
{
    //定义相应的数据
    WSADATA wsaData;
    SOCKET sClient;
    int iPort = 5050;//对应的服务端的端口
    int iLen, iSend;
    char buf[1024];
    struct sockaddr_in ser;

    //启动 winSocket
    memset(buf, 0, sizeof(buf));
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cout << "Failed to load Winsock\n";
        system("pause");
        return;
    }

    //char addr[20] = { '\0' };
    //std::cin >> addr;//输入服务端 IP 地址
    char addr[20] = "192.168.1.119";

    //创建Socket
    ser.sin_family = AF_INET;
    ser.sin_port = htons(iPort);
    inet_pton(AF_INET, addr, (void*)&ser.sin_addr.s_addr);
    sClient = socket(AF_INET, SOCK_STREAM, 0);
    if (sClient == INVALID_SOCKET) {
        std::cout << "socket() Failed\n";
        system("pause");
        return;
    }

    //连接并进行简单的操作
    if (connect(sClient, (struct sockaddr*)&ser, sizeof(ser)) == INVALID_SOCKET) {
        std::cout << "connect() Failed\n";
        system("pause");
        return;
    }
    else {
        //接收服务端发送的数据
        iLen = recv(sClient, buf, sizeof(buf), 0);
        if (iLen == 0) {
            system("pause");
            return;
        }
        else if (iLen == SOCKET_ERROR) {
            std::cout << "recv() Failed\n";
            system("pause");
            return;
        }
        std::cout << "recv() data from server:" << buf << std::endl;
    }

    //对服务端发送指令或者数据
    int index = 0;//此数不断递增,然后发送给服务端,用于验证多线程是否有用
    while (true) {
        //输入相应的数据发送给服务端
        //std::cout << ">>";
        //std::cin >> buf;

        //将数字转换为字符串
        _itoa_s(index, buf, 10);
        ++index;

        //发送数据到服务端
        iSend = send(sClient, buf, sizeof(buf), 0);
        if (iSend == SOCKET_ERROR) {
            std::cout << "send() Failed\n";
        }
        else if (iSend == 0) {
            std::cout << "send() Zero\n";
        }
        else {
            std::cout << "Send byte:" << iSend << std::endl;
            std::cout << "----------------------------------\n";
        }
    }

    //关闭连接并退出
    closesocket(sClient);
    //关闭 Winsock
    WSACleanup();

    system("pause");
}

运行

    首先启动服务端,然后启动多个客户端,因为启动的时间不一样,所以各个客户端在服务端显示的数字是相差还是比较大的

FTP服务器和客户端 《计算机通信网络》课程项目 终期报告 题 目: FTP协议实现 组 号: 第7组 任课教师: 王瑞 FT 2019年03月01日 FTP协议实现 一.项目介绍 文件传输协议(File Transfer Protocol, FTP)是重要的应用层协议,用于Internet上控制文件的双向传输。FTP允许用户连接一台远程计算机... 阅读详情

相关推荐

Linux系统ftp服务设置

FTP全称为File Transfer Protocol,是一种在计算机网络上常用的文件传输协议。FTP协议通过TCP连接传输文件,可以用于在不同的操作系统之间传输文件。在FTP服务中,有两个角色:FTP服务器和FTP客户端。FTP服务器是负责存储文件并对客户端用户进行认证和授权的主机,而FTP客户端则是用户使用的工具,可以通过FTP协议连接到FTP服务器,上传或下载文件。一般情况下,FTP服务器通常放在远程主机上,用户需要通过FTP客户端软件连接到该主机才能进行文件传输。

2301_78160271的博客 1万+

多线程FTP项目(3)—— socketserver版本多线程FTP项目

多线程FTP项目(3)—— socketserver版本多线程FTP项目 threading 版 ​ 本来是想自己写一个实现多进程的 FTP 项目的,也就是说不使用 socketserver 模块实现多线程 FTP 项目,但是我写到一半调试的时候发现,虽然可以实现多用户同时登录,但是在输入命令之后,客户端很容易被 “远程计算机直接断开连接”。目前还是不清楚出了什么问题,不过看了 socketserver 模块源码后,发现该模块的多线程实现是比较复杂的,所以我觉得出现这个 bug 很大可能是因为

ShiJieDeYinYu的博客 738

基于Python的ftp文件服务器(附代码)

设计流程:    1、设计:使用多线程并发的TCP传输模式,实现客户端获取文件列表、上传文件、下载文件。  2、计划实施:今天完成项目  3、文档确认         使用方法:命令行方法获取文件目录、文件上传、文档下载         代码架构:             创建并发的网络连接 + 退出处理 + 测试              实现查看文件列表的功能                ...

WilliamCode的博客 2889

多线程FTP项目(4)—— Mysql数据库 + FTP

多线程FTP项目(4)——Mysql数据库+FTP。

ShiJieDeYinYu的博客 511

深入探索FTP客户端开发:多线程、文件管理与C++/MFC实现

本文还有配套的精品资源,点击获取 简介:本文深入探讨了FTP客户端的关键特性,包括多线程技术以提高文件传输效率、文件和文件夹操作、用户管理和多客户端登录功能。我们将重点介绍使用C++和MFC库开发FTP客户端的实践,以及如何处理并发操作和用户界面设计。此外,还会涉及到理解与服务器交互的FTP协议和通信细节。 1. FTP客户端的核心特性 1.1 FTP客户端...

weixin_35734408的博客 1257

C语言实现的FTP客户端项目教程

网络编程的核心在于能够在不同主机间进行数据交换,而套接字(Sockets)则是实现这一功能的关键。套接字编程是网络通信的基石,它允许一台计算机上的程序与另一台计算机上的程序进行数据传输。在C语言中,套接字编程通常涉及以下几个步骤:创建套接字绑定套接字到一个地址和端口监听连接接受连接发送和接收数据关闭套接字libcurl是一个用于客户端URL传输的免费、易于使用的客户端端库。它支持多种协议,包括HTTP、HTTPS、FTP、SFTP等。

weixin_42593549的博客 992

C++的简单FTP客户端实现(二)编程

基于QT,实现的不是一个功能全的FTP客户端,而是程序中有从FTP服务器下载文件的需求,主要实现了下载的功能,包括断点续传,没有实现多线程下载。

余金兑人的技术角落 7703

FlashFXP:全面的FTP客户端工具

本文还有配套的精品资源,点击获取 简介:FlashFXP是一款高效且安全的FTP客户端软件,支持多种文件传输协议,包括FTP、FTPS和SFTP,适用于Windows系统。该软件简化了文件上传与下载过程,提供了便捷的站点管理、多线程传输、断点续传、加密传输和脚本支持等功能。对于开发人员和网站管理员来说,FlashFXP的快速部署代码功能特别有用,使文件传输更加简单高效。 ...

weixin_36364707的博客 2126

7个免费的Linux FTP客户端工具

在Dropbox、YouSendIt、idrive以及许多这样云存储和共享工具的帮助下,我们在互联网上发送和共享大型文件变得容易起来。所有这些网站都可以帮助你在互联网上传送文件,但如果你要分享庞大的数据,这依然是很复杂的事情。所以,你需要装配FTP服务器,它能让你轻松地共享这些文件。   FTP服务器支持文件传输协议,这是互联网交换程序和数据文件的标准。基本上讲FTP站点是提供文件下载的网站...

ihoudf 8987

C# FTP客户端开发完整指南

文件传输协议(FTP)是互联网上使用最为广泛的文件传输标准之一,它允许网络用户在客户端和服务器之间传输文件。在C#中实现一个基础的FTP客户端,开发者需要熟悉.NET框架中的相关类库。在本章中,我们深入探讨了C# FTP客户端在代码质量和安全性方面的实践。我们学习了如何提升代码的可读性和维护性,如何通过良好的错误处理机制来构建健壮的程序,以及如何利用FTPS/SFTP提高应用程序的安全性。这些知识点对于任何希望构建稳定且安全的FTP客户端的开发者都是至关重要的。本文还有配套的精品资源,点击获取。

weixin_42181686的博客 1192

构建Linux环境下的FTP服务器与客户端

Linux操作系统自1991年诞生以来,已经成为了全球服务器市场的主流选择。其开源的本质,使得它能被广泛修改和扩展,以适应各种不同的使用需求。Linux系统以其稳定性、高效率和安全性被IT行业所推崇,它支持从个人电脑到超级计算机的各种硬件平台。本章我们将探讨Linux的发展历程,以及它在技术上的几个关键特点。例如,它是多用户、多任务的,能够支持各种各样的文件系统和硬件设备。通过了解Linux系统的不同版本(如Ubuntu、CentOS、Debian等),我们可以确定它在不同的使用场景下的最佳实践。

weixin_42584758的博客 837

FTP服务

FTP:文件传输协议(File Transfer Protocol 简称FTP),是一种用于在网络上进行文件传输的标准协议。它允许用户通过网络从一个计算机向另一个计算机传输文件。FTP 是互联网早期的基本协议之一,提供了一种可靠、简单的方式来共享文件。采用c/s模式。控制端口:21;数据端口:20。

2301_78088515的博客 7620

FTP多线程上传+进度条

自己在网上找找学习写的 以前找了好多ftp+多线程进度条上传 没全的,自己结合好多知识写的 希望对有帮助的人学习

ftp 命令与服务

随着互联网和网络技术的发展,FTP 服务器也不断演变。FTP 服务器的发展从简单的文件传输协议,逐步演变为支持加密、用户认证、多协议支持的现代化工具。​: 设置 FTP 服务器允许的最大并发连接数,以及每个 IP 地址允许的最大连接数。通过 FTP 客户端(如 FileZilla、WinSCP 等)使用主机 IP 地址、用户名和密码连接到 FTP 服务器。​: 设置 FTP 服务器监听的端口,默认是 21。如果使用防火墙,确保允许 FTP 服务器的相关端口(默认情况下,FTP 使用 TCP 端口 21)。

qq_38641599的博客 1978

UE5 Compute Shader实战:从基础到高级应用全解析

本文全面解析了在UE5中应用Compute Shader的完整流程,从基础概念、环境搭建到核心机制与实战应用。详细介绍了如何通过参数传递、线程派遣与数据读写实现GPU并行计算,并深入探讨了GPU粒子模拟、性能优化及结合光线追踪等高级应用场景,为开发者掌握UE5高性能计算提供系统指导。

yhn45678901的博客 499

Ad hoc网络路由协议概述4——按需路由协议(2)AODV协议 (Ad-hoc on-demand distance vector algorithm protocol)

AODV协议的基本流程。

weixin_42982240的博客 7705

【免费下载】 FileZilla Pro 3.64:Windows系统下的FTP客户端利器

FileZilla Pro 3.64:Windows系统下的FTP客户端利器 【下载地址】FTP客户端FileZillaPro3.64版本在Windows系统的下载与安装配置 FileZilla是一款免费的FTP客户端,支持多协议如FTP、FTPS、SFTP等,适用于多种操作系统。FileZilla Pro是FileZi...

gitblog_06556的博客 1361

华为交换机与思科/华三设备LACP对接实战:手把手教你配置链路聚合

本文详细解析了华为交换机与思科、华三设备间的LACP链路聚合配置实战,涵盖核心原理、配置步骤及排错技巧。通过对比不同厂商的实现差异,提供可落地的跨厂商对接方案,助力企业网络工程师高效部署高可靠性链路聚合。

weixin_30836759的博客 422

基于Java实现的一个FTP服务

本文实现了一个基于Java的FTP与Web服务器系统,支持TCP/UDP协议的文件传输及HTTP网页访问,具备多线程非阻塞处理能力。系统包含FTP服务器、客户端及Web服务器,满足文件查看、上传、下载等需求,注重响应速度、稳定性和可维护性。项目以锻炼网络编程与系统设计能力为目标,采用Eclipse开发环境,遵循良好编码规范,提供完整用例与接口设计。资源可通过优快云链接下载。

毕业作品网站 2590

人工智能训练师三级考试大纲深度解读|理论+实操分值分布+六大模块占比

三级人工智能训练师考试分为理论和实操两大模块(各100分,合格线60分)。理论考试包含AI基础(30%)、数据标注(25%)、模型训练(20%)、测试(15%)和系统运维(10%),题型为客观题;实操考核侧重数据标注(40%)、模型测试(35%)和系统运维(25%),采用现场操作形式。复习应优先关注AI基础和数据标注等高分值模块,实操需重点掌握标准流程和结果准确性(占评分70%)。考试要求两模块均达标,理论侧重概念理解,实操强调规范执行与文档完整。

白话机器学习 1080

isaaclab无法识别NVIDIA显卡,如何解决?

🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀!    📌 特别说明: 文中部分技术问题来源于真实生产环境及网络公开案例,均经过精挑细选与系统化整理,并结合多位一线资深架构师和工程师多年实战经验沉淀,提

**My Coding Family** 1144
上一篇: FTP 编写 2:客户端与服务端的连接
下一篇: FTP 编写 4:命令解析
_萧_
博客等级 码龄10年 55粉丝 157原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值