• TCP通信


    TCP通信的文件上传案例


    • 本地流:客户端和服务器和本地硬盘进行读写,需要使用自己创建的字节流

    • 网络流:客户端和服务器之间读写,必须使用Socket中提供的字节流对象

    客户端工作:读取本地文件,上传到服务器,读取服务器回写的数据


    • 明确数据源
    • 目的地:服务器

    • 客户端代码:
    package cn.learn.web;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class client {
        public static void main(String[] args) throws IOException {
            //创建一个本地的字节输入流,构造方法绑定数据源
            FileInputStream fileIn = new FileInputStream("b.txt");
    
    
            //*****发送数据******
            //创建客户端对象Socket,构造方法绑定服务器IP地址和端口号
            Socket socket = new Socket("127.0.0.1", 8020);
            //使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
            OutputStream socketOut = socket.getOutputStream();
    
    
            //读入内存
            int len = 0;
            byte[] bytes = new byte[1024];
            //使用流中的write方法给服务器发送数据
            while ((len = fileIn.read(bytes)) != -1) {
                socketOut.write(bytes, 0, len);
            }
    
    
            //已上传完文件,但read()导致服务器阻塞,给服务器写一个结束标记
            socket.shutdownOutput();
            
            //使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
            InputStream clientIn = socket.getInputStream();
            //使用InputStream对象中的read()方法,读取服务器回写的数据
            while ((len = clientIn.read(bytes)) != -1) {
                //打印看看
                System.out.println(new String(bytes, 0, len));
            }
            
            
            
            //释放资源,只关闭Socket的IO流就行
            fileIn.close();
            socket.close();
    
        }
    }
    
    

    服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写“上传成功”


    • 明确:
      1. 数据源:客户端上传的文件
      2. 目的地:服务器的硬盘 d:\upload.txt

    • 服务器代码:
    package cn.learn.web;
    
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class Server {
        public static void main(String[] args) throws IOException {
    
            //设置通信端口号,不然系统随机分配
            ServerSocket server = new ServerSocket(8020);
    
            //使用serverSocket对象中的方法accept,获取到请求的客户端对象Socket(含地址和端口号)
            Socket socket1 = server.accept();
    
            //使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
            InputStream serveIn = socket1.getInputStream();
    
    
            //判断文件夹是否存在,若不存在新建
            File file = new File("d:\upload");
            if(!file.exists()){
                file.mkdir();
            }
    
            //创建服务器的本地字节输出流,构造方法绑定输出位置,注意****\******
            FileOutputStream localOut = new FileOutputStream(file+"\b.txt");
    
            //获取读取的数据有效长度,循环读取和写入服务器硬盘
            byte[] bytes = new byte[1024];
            int len = 0;
            //使用serveIn的方法read,读取客户端发送的数据
            while ((len = serveIn.read(bytes)) != -1) {
                localOut.write(bytes,0,len);
                //打印到控制台看看
                System.out.println(new String(bytes, 0, len));
    
            }
    
            //使用Socket对象中的方法getOutputStream()获取网络字节输入流OutputStream对象
            OutputStream serverOut = socket1.getOutputStream();
    
            //6.使用serverOut中的write方法回写给客户端
            serverOut.write("我收到了,上传成功".getBytes());
    
            //7.释放socket1与server的流
            server.close();
            socket1.close();
        }
    }
    
    

    注:服务器一直开启,等待客户端上传


    • TCP通信完成后服务器一直未停止原因

      1. read()方法,若没有输入可用,方法阻塞
      2. 具体原因是read()读取时用的循环,读不到结束标记,服务器一直等待进入阻塞状态

    //已上传完文件,但read()导致阻塞,给服务器写一个结束标记
    socket.shutdownOutput();

    上述代码优化


    • 优化服务器存储文件名称
    
            /*
            自定义服务器本地存储文件名称,防止冲突覆盖
            规则:域名+毫秒值+随机数
             */
            String filename = "learn" + System.currentTimeMillis()+ (new Random().nextInt(10)+1)+".txt";
    
            //创建服务器的本地字节输出流,构造方法绑定输出位置,注意****\******
            FileOutputStream localOut = new FileOutputStream(file+"\"+filename);
    
    • 使服务器一直处于监听状态,循环accept接受的套接字操作(而不是阻塞),最后不用关闭服务器了

    • 在循环中使用多线程,提高效率,有一个客户端上传文件,就开启一个线程。将代码块复制到run()方法中,注意使用try catch抛出异常

    服务器代码优化,开启多线程

    package cn.learn.web;
    
    
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Random;
    
    
    public class Server {
        public static void main(String[] args) throws IOException {
    
            //设置通信端口号,不然系统随机分配
            ServerSocket server = new ServerSocket(8020);
    
            /*
            循环,使得服务器一直处于监听状态,有客户端上传文件,就存入
             */
            while (true) {
    
                //使用serverSocket对象中的方法accept,获取到请求的客户端对象Socket(含地址和端口号)
                Socket socket1 = server.accept();
    
                /******
                 客户端每上传一个文件,就开启一个多线程
                 ******/
                new Thread(new Runnable() {
    
                    @Override
                    public void run() {
                        try {
                            //使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
                            InputStream serveIn = socket1.getInputStream();
    
    
                            //判断文件夹是否存在,若不存在新建
                            File file = new File("d:\upload");
                            if (!file.exists()) {
                                file.mkdir();
                            }
    
                            /*
                            自定义服务器本地存储文件名称,防止冲突覆盖
                            规则:域名+毫秒值+随机数
                            */
                            String filename = "learn" + System.currentTimeMillis() + (new Random().nextInt(10) + 1) + ".txt";
    
                            //创建服务器的本地字节输出流,构造方法绑定输出位置,注意****\******
                            FileOutputStream localOut = new FileOutputStream(file + "\" + filename);
    
                            //获取读取的数据有效长度,循环读取和写入服务器硬盘
                            byte[] bytes = new byte[1024];
                            int len = 0;
                            //使用serveIn的方法read,读取客户端发送的数据
                            while ((len = serveIn.read(bytes)) != -1) {
                                localOut.write(bytes, 0, len);
                            }
    
    
                            //使用Socket对象中的方法getOutputStream()获取网络字节输入流OutputStream对象
                            OutputStream serverOut = socket1.getOutputStream();
    
                            //6.使用serverOut中的write方法回写给客户端
                            serverOut.write("我收到了,上传成功".getBytes());
    
                            //7.释放socket1与server的流
                            socket1.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        }
    }
    
    
    
  • 相关阅读:
    iOS 7 开发注意事项
    iPhone消息推送机制实现与探讨
    iPhone开发之全景展示(panoramagl)
    cocos2d-x安装和卸载
    使用Objective-C的+(void)initialize初始化static变量
    Objective C类方法load和initialize的区别
    使用Xcode和Instruments调试解决iOS内存泄露
    NSArray的三种排序方法
    Cocoa:NSOperation和NSOperationQueue
    NSNotification、delegate和KVO的区别
  • 原文地址:https://www.cnblogs.com/huxiaobai/p/11614101.html
Copyright © 2020-2023  润新知