C#网络编程(接收文件) - Part.5

源地址:http://www.tracefact.net/CSharp-Programming/Network-Programming-Part5.aspx


这篇文章将完成Part.4中剩余的部分,它们本来是一篇完整的文章,但是因为上一篇比较长,合并起来页数太多,浏览起来可能会比较不方便,我就将它拆为两篇了,本文便是它的后半部分。我们继续进行上一篇没有完成的步骤:客户端接收来自服务端的文件。

4.客户端接收文件

4.1服务端的实现

对于服务端,我们只需要实现上一章遗留的sendFile()方法就可以了,它起初在handleProtocol中是注释掉的。另外,由于创建连接、获取流等操作与receiveFile()是没有区别的,所以我们将它提出来作为一个公共方法getStreamToClient()。下面是服务端的代码,只包含新增改过的代码,对于原有方法我只给出了签名:

class Server {
    static void Main(string[] args) {
        Console.WriteLine("Server is running ... ");
        IPAddress ip = IPAddress.Parse("127.0.0.1");
        TcpListener listener = new TcpListener(ip, 8500);

        listener.Start();           // 开启对控制端口 8500 的侦听
        Console.WriteLine("Start Listening ...");

        while (true) {
            // 获取一个连接,同步方法,在此处中断
            TcpClient client = listener.AcceptTcpClient();  
            RemoteClient wapper = new RemoteClient(client);
            wapper.BeginRead();
        }
    }
}

public class RemoteClient {
    // 字段 略

    public RemoteClient(TcpClient client) {}

    // 开始进行读取
    public void BeginRead() { }

    // 再读取完成时进行回调
    private void OnReadComplete(IAsyncResult ar) { }

    // 处理protocol
    private void handleProtocol(object obj) {
        string pro = obj as string;
        ProtocolHelper helper = new ProtocolHelper(pro);
        FileProtocol protocol = helper.GetProtocol();

        if (protocol.Mode == FileRequestMode.Send) {
            // 客户端发送文件,对服务端来说则是接收文件
            receiveFile(protocol);
        } else if (protocol.Mode == FileRequestMode.Receive) {
            // 客户端接收文件,对服务端来说则是发送文件
            sendFile(protocol);
        }
    }

    // 发送文件
    private void sendFile(FileProtocol protocol) {
        TcpClient localClient;
        NetworkStream streamToClient = getStreamToClient(protocol, out localClient);

        // 获得文件的路径
        string filePath = Environment.CurrentDirectory + "/" + protocol.FileName;

        // 创建文件流
        FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        byte[] fileBuffer = new byte[1024];     // 每次传1KB
        int bytesRead;
        int totalBytes = 0;

        // 创建获取文件发送状态的类
        SendStatus status = new SendStatus(filePath);

        // 将文件流转写入网络流
        try {
            do {
                Thread.Sleep(10);           // 为了更好的视觉效果,暂停10毫秒:-)
                bytesRead = fs.Read(fileBuffer, 0, fileBuffer.Length);
                streamToClient.Write(fileBuffer, 0, bytesRead);
                totalBytes += bytesRead;            // 发送了的字节数
                status.PrintStatus(totalBytes); // 打印发送状态
            } while (bytesRead > 0);
            Console.WriteLine("Total {0} bytes sent, Done!", totalBytes);
        } catch {
            Console.WriteLine("Server has lost...");
        }

        streamToClient.Dispose();
        fs.Dispose();
        localClient.Close();
    }

    // 接收文件
    private void receiveFile(FileProtocol protocol) { }

    // 获取连接到远程的流 -- 公共方法
    private NetworkStream getStreamToClient(FileProtocol protocol, out TcpClient localClient) {
        // 获取远程客户端的位置
        IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint;
        IPAddress ip = endpoint.Address;

        // 使用新端口号,获得远程用于接收文件的端口
        endpoint = new IPEndPoint(ip, protocol.Port);

        // 连接到远程客户端
        try {
            localClient = new TcpClient();
            localClient.Connect(endpoint);
        } catch {
            Console.WriteLine("无法连接到客户端 --> {0}", endpoint);
            localClient = null;
            return null;
        }

        // 获取发送文件的流
        NetworkStream streamToClient = localClient.GetStream();
        return streamToClient;
    }

    // 随机获取一个图片名称
    private string generateFileName(string fileName) {}
}

服务端的sendFile方法和客户端的SendFile()方法完全类似,上面的代码几乎是一次编写成功的。另外注意我将客户端使用的SendStatus类也拷贝到了服务端。接下来我们看下客户端。

4.2客户端的实现

首先要注意的是客户端的SendFile()接收的参数是文件全路径,但是在写入到协议时只获取了路径中的文件名称。这是因为服务端不需要知道文件在客户端的路径,所以协议中只写文件名;而为了使客户端的SendFile()方法更通用,所以它接收本地文件的全路径。

客户端的ReceiveFile()的实现也和服务端的receiveFile()方法类似,同样,由于要保存到本地,为了避免文件名重复,我将服务端的generateFileName()方法复制了过来。

public class ServerClient :IDisposable {
    // 字段略

    public ServerClient() {}

    // 发送消息到服务端
    public void SendMessage(string msg) {}

    // 发送文件 - 异步方法
    public void BeginSendFile(string filePath) {    }

    private void SendFile(object obj) { }
    
    // 发送文件 -- 同步方法
    public void SendFile(string filePath) {}
    
    // 接收文件 -- 异步方法
    public void BeginReceiveFile(string fileName) {
        ParameterizedThreadStart start =
            new ParameterizedThreadStart(ReceiveFile);
        start.BeginInvoke(fileName, nullnull);
    }

    public void ReceiveFile(object obj) {
        string fileName = obj as string;
        ReceiveFile(fileName);
    }

    // 接收文件 -- 同步方法
    public void ReceiveFile(string fileName) {

        IPAddress ip = IPAddress.Parse("127.0.0.1");
        TcpListener listener = new TcpListener(ip, 0);
        listener.Start();

        // 获取本地侦听的端口号
        IPEndPoint endPoint = listener.LocalEndpoint as IPEndPoint;
        int listeningPort = endPoint.Port;

        // 获取发送的协议字符串
        FileProtocol protocol =
            new FileProtocol(FileRequestMode.Receive, listeningPort, fileName);
        string pro = protocol.ToString();

        SendMessage(pro);       // 发送协议到服务端

        // 中断,等待远程连接
        TcpClient localClient = listener.AcceptTcpClient();
        Console.WriteLine("Start sending file...");
        NetworkStream stream = localClient.GetStream();

        // 获取文件保存的路劲
        string filePath =
            Environment.CurrentDirectory + "/" + generateFileName(fileName);

        // 创建文件流
        FileStream fs = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
        byte[] fileBuffer = new byte[1024];     // 每次传1KB
        int bytesRead;
        int totalBytes = 0;

        // 从缓存buffer中读入到文件流中
        do {
            bytesRead = stream.Read(buffer, 0, BufferSize);
            fs.Write(buffer, 0, bytesRead);
            totalBytes += bytesRead;
            Console.WriteLine("Receiving {0} bytes ...", totalBytes);
        } while (bytesRead > 0);

        Console.WriteLine("Total {0} bytes received, Done!", totalBytes);

        fs.Dispose();           
        stream.Dispose();
        localClient.Close();
        listener.Stop();
    }


    // 随机获取一个图片名称
    private string generateFileName(string fileName) {}

    public void Dispose() {
        if (streamToServer != null)
            streamToServer.Dispose();
        if (client != null)
            client.Close();
    }
}

上面关键的一句就是创建协议那句,注意到将mode由Send改为了Receive,同时传去了想要接收的服务端的文件名称。

4.3程序测试

现在我们已经完成了所有收发文件的步骤,可以看到服务端的所有操作都是被动的,接下来我们修改客户端的Main()程序,创建一个菜单,然后根据用户输入发送或者接收文件。

class Program {
    static void Main(string[] args) {

        ServerClient client = new ServerClient();
        string input;
        string path = Environment.CurrentDirectory + "/";

        do {
            Console.WriteLine("Send File:    S1 - Client01.jpg, S2 - Client02.jpg, S3 - Client03.jpg");
            Console.WriteLine("Receive File: R1 - Server01.jpg, R1 - Server02.jpg, R3- Server03.jpg");
            Console.WriteLine("Press 'Q' to exit. \n");
            Console.Write("Enter your choice: ");
            input = Console.ReadLine();
            switch(input.ToUpper()){
                case "S1":
                    client.BeginSendFile(path + "Client01.jpg");
                    break;
                case "S2":
                    client.BeginSendFile(path + "Client02.jpg");
                    break;
                case "S3":
                    client.BeginSendFile(path + "Client02.jpg");
                    break;
                case "R1":
                    client.BeginReceiveFile("Server01.jpg");
                    break;
                case "R2":
                    client.BeginReceiveFile("Server01.jpg");
                    break;
                case "R3":
                    client.BeginReceiveFile("Server01.jpg");
                    break;
            }               
        } while (input.ToUpper() != "Q");

        client.Dispose();
    }
}

由于这是一个控制台应用程序,并且采用了异步操作,所以这个菜单的出现顺序有点混乱。我这里描述起来比较困难,你将代码下载下来后运行一下就知道了:-)

程序的运行结果和上一节类似,这里我就不再贴图了。接下来是本系列的最后一篇,将发送字符串与传输文件的功能结合起来,创建一个可以发送消息并能收发文件的聊天程序,至于语音聊天嘛...等我学习了再告诉你 >_<、


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: instantclient-basic-windows.x64-12.1.0.2.0.zip是一个Oracle数据库的客户端程序包。这个程序包是提供给Windows 64位系统用户使用的,它在12.1.0.2.0版本上运行。它包含了Oracle数据库的客户端库文件,可以用来连接和操作Oracle数据库。 使用这个客户端程序包,用户可以在自己的计算机上进行开发和运行与Oracle数据库相关的应用程序。它提供了一系列的库文件和工具,用于连接数据库、执行SQL语句、管理数据库连接等操作。 该程序包的安装非常简单,只需解压缩即可获得所需的文件。在解压缩后,用户可以将这些文件放在指定的目录中,然后将该目录添加到系统的PATH环境变量中,以便在命令行终端或其他程序中调用这些库文件。 通过安装instantclient-basic-windows.x64-12.1.0.2.0.zip,用户可以轻松地连接到Oracle数据库,并使用各种编程语言(如Java、Python等)编写与数据库交互的应用程序。无论是开发Web应用、企业级应用还是数据分析,都可以通过这个客户端程序包实现与Oracle数据库的连接和操作。 总的来说,instantclient-basic-windows.x64-12.1.0.2.0.zip提供了Oracle数据库的基本客户端功能,可以帮助用户在Windows操作系统上与Oracle数据库进行交互。 ### 回答2: instantclient-basic-windows.x64-12.1.0.2.0.zip 是一个软件包文件的名称,它是为了在Windows系统上使用的Oracle Instant Client提供支持的。Oracle Instant Client是一个轻量级的数据库客户端,允许用户在不安装完整版Oracle数据库的情况下访问和运行Oracle数据库应用程序。 这个软件包文件非常重要,因为它包含了安装Oracle Instant Client所需的基本文件。安装这个软件包后,用户就可以在Windows系统上使用Oracle Instant Client,并通过其提供的API和命令行工具来连接和管理Oracle数据库。 这个软件包文件的具体版本是12.1.0.2.0,适用于64位的Windows系统。它是一个压缩文件(ZIP),需要先下载并解压缩才能使用。解压后,你会看到其中包含了一些必要的文件文件夹,如OCI库文件、头文件和示例文件等。 使用这个软件包安装Oracle Instant Client的过程相对简单。首先,你需要将软件包文件解压到一个合适的文件夹中。然后,根据需要将相应的文件夹(如bin文件夹)添加到系统路径中,以便系统可以找到Oracle Instant Client的可执行文件。接下来,你可以使用Oracle Instant Client提供的各种工具和API来连接和操作Oracle数据库。 总之,instantclient-basic-windows.x64-12.1.0.2.0.zip 是一个支持在64位Windows系统上使用Oracle Instant Client的软件包文件,通过安装这个软件包,用户可以在Windows系统上轻松实现对Oracle数据库的访问和运行。 ### 回答3: instantclient-basic-windows.x64-12.1.0.2.0.zip是Oracle公司提供的一个软件包,用于在Windows 64位操作系统上安装和配置Oracle数据库客户端。这个软件包是基本的客户端安装包,包含了运行Oracle数据库客户端所需的必要文件和组件。 使用该软件包,用户可以将其解压缩到自定义目录中,并进行相关配置,然后通过Oracle数据库客户端访问远程或本地的Oracle数据库服务器。这个软件包为开发人员和系统管理员提供了一种便捷的方式来连接和管理数据库。 安装这个软件包需要确保系统已经安装了合适的Java运行环境,并且正确配置了系统变量,以便正确运行Oracle数据库客户端。安装过程主要包括解压缩软件包、设置环境变量、配置连接信息等步骤。 通过安装instantclient-basic-windows.x64-12.1.0.2.0.zip,用户可以使用Oracle提供的工具和功能来进行数据库开发、管理和调试。这个软件包也支持各种编程语言,包括Java、C++、C#等,使得开发人员可以方便地使用自己熟悉的语言进行数据库编程。 总之,instantclient-basic-windows.x64-12.1.0.2.0.zip提供了一个完整的Oracle数据库客户端安装包,方便用户在Windows 64位操作系统上进行数据库开发和管理工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值