时隔两年,我又拿起了网络编程的socket

基于tcp协议的C/S架构文件上传案例

demo代码

/**
 * 文件上传客户端,读取本地文件,上传给服务器
 *
 * 数据源:本地文件
 *目的:服务器
 *
 *步骤:
 * 1.创建客户端对象
 * 2.获取本地文件输入流读取到Byte数组中
 * 3.获取socket文件输出流 上传到服务器中
 * 4.获取socket文件输入流回显服务器返回的信息,打印
 * 5.关闭资源
 */
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建客户端对象
        Socket socket = new Socket("127.0.0.1", 8888);

        //2.获取本地文件输入流读取到Byte数组中
        File file = new File("E:\\data\\project\\tcp-study\\clientImg\\1596592777(1).jpg");
        FileInputStream is = new FileInputStream(file);
        byte[] bytes = new byte[1024];
        int len=0;

        //3.获取socket文件输出流 上传到服务器中
        OutputStream os = socket.getOutputStream();
        while ((len=is.read(bytes))!=-1){
            os.write(bytes,0,len);
        }

        //4.获取socket文件输入流回显服务器返回的信息,打印
        InputStream socketInputStream = socket.getInputStream();
        while ((len=socketInputStream.read(bytes))!=-1){
            System.out.println(new String(bytes, 0, len));
        }

        //5.关闭资源
        socketInputStream.close();
        os.close();
        is.close();
        socket.close();
    }
}
/**
 * 文件上传服务器,获取客户端上传的文件,保存在本地中
 *
 * 步骤:
 * 1.获取服务器对象
 * 2.获取socket对象
 * 3.根据socket获取客户端输入流
 * 4.读取输入流,同时用本地输出流输出到本地文件夹中保存
 * 5.获取socket输出流输出保存成功的信息给客户端
 * 6.关闭资源
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1.获取服务器对象
        ServerSocket serverSocket = new ServerSocket(8888);
        //2.获取socket对象
        Socket socket = serverSocket.accept();
        //3.根据socket获取客户端输入流
        InputStream socketInputStream = socket.getInputStream();

        //4.读取输入流,同时用本地输出流输出到本地文件夹中保存
        byte[] bytes = new byte[1024];
        int len =0;
        File file = new File("E:\\data\\project\\tcp-study\\serverImg");
        //如果文件夹不存在,则创建
        if (!file.exists()){
            file.createNewFile();
        }
        FileOutputStream fileOutputStream = new FileOutputStream(file+File.separator+"1.jpg");
        while ((len=socketInputStream.read(bytes))!=-1){
            fileOutputStream.write(bytes,0,len);
        }

        //5.获取socket输出流输出保存成功的信息给客户端
        OutputStream socketOutputStream = socket.getOutputStream();
        socketOutputStream.write("保存成功".getBytes());

        //6.关闭资源
        socketOutputStream.close();
        socketInputStream.close();
        fileOutputStream.close();
        socket.close();
        serverSocket.close();
    }
}

但是这样写文件会实现上传功能,但是程序会进入阻塞状态因为在客户端代码中,while ((len=is.read(bytes))!=-1)

客户端读取到文件的末尾,但是不告诉服务器已经结束了,结果服务器一直在等,不发送保存成功的信息,导致客户端也在等,所以客户端和服务器都进入阻塞状态。

解决客户端不发送结束语导致程序IO阻塞

在客户端while ((len=is.read(bytes))!=-1) 后,使用socket.shutdownOutput();告诉服务器已经结束了。

/**
 * 文件上传客户端,读取本地文件,上传给服务器
 *
 * 数据源:本地文件
 *目的:服务器
 *
 *步骤:
 * 1.创建客户端对象
 * 2.获取本地文件输入流读取到Byte数组中
 * 3.获取socket文件输出流 上传到服务器中
 * 4.获取socket文件输入流回显服务器返回的信息,打印
 * 5.关闭资源
 */
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建客户端对象
        Socket socket = new Socket("127.0.0.1", 8888);

        //2.获取本地文件输入流读取到Byte数组中
        File file = new File("E:\\data\\project\\tcp-study\\clientImg\\1596592777(1).jpg");
        FileInputStream is = new FileInputStream(file);
        byte[] bytes = new byte[1024];
        int len=0;

        //3.获取socket文件输出流 上传到服务器中
        OutputStream os = socket.getOutputStream();
        while ((len=is.read(bytes))!=-1){
            os.write(bytes,0,len);
        }
        
        //告诉服务器结束了
        socket.shutdownOutput();

        
        //4.获取socket文件输入流回显服务器返回的信息,打印
        InputStream socketInputStream = socket.getInputStream();
        while ((len=socketInputStream.read(bytes))!=-1){
            System.out.println(new String(bytes, 0, len));
        }

        //5.关闭资源
        socketInputStream.close();
        os.close();
        is.close();
        socket.close();
    }
}

优化服务器代码

  1. 图片保存命名规则,防止重名
  2. 优化服务器只能接受一次图片就结束,用while循环,不关闭资源
/**
 * 文件上传服务器,获取客户端上传的文件,保存在本地中
 *
 * 步骤:
 * 1.获取服务器对象
 * 2.获取socket对象
 * 3.根据socket获取客户端输入流
 * 4.读取输入流,同时用本地输出流输出到本地文件夹中保存
 * 5.获取socket输出流输出保存成功的信息给客户端
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1.获取服务器对象
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true){
            //2.获取socket对象
            Socket socket = serverSocket.accept();
            //3.根据socket获取客户端输入流
            InputStream socketInputStream = socket.getInputStream();

            //4.读取输入流,同时用本地输出流输出到本地文件夹中保存
            byte[] bytes = new byte[1024];
            int len =0;
            //自定义命名规则,防止重名
            String fileName="laowang"+System.currentTimeMillis()+ new Random().nextInt(9999)+".jpg";
            File file = new File("E:\\data\\project\\tcp-study\\serverImg");
            //如果文件夹不存在,则创建
            if (!file.exists()){
                file.createNewFile();
            }
            FileOutputStream fileOutputStream = new FileOutputStream(file+File.separator+fileName);
            while ((len=socketInputStream.read(bytes))!=-1){
                fileOutputStream.write(bytes,0,len);
            }

            //5.获取socket输出流输出保存成功的信息给客户端
            OutputStream socketOutputStream = socket.getOutputStream();
            socketOutputStream.write(("保存成功,url为:"+file.getCanonicalPath()+fileName).getBytes());
        }
    }
}

服务器端使用多线程提高效率

/**
 * 文件上传服务器,获取客户端上传的文件,保存在本地中
 *
 * 步骤:
 * 1.获取服务器对象
 * 2.获取socket对象
 * 3.根据socket获取客户端输入流
 * 4.读取输入流,同时用本地输出流输出到本地文件夹中保存
 * 5.获取socket输出流输出保存成功的信息给客户端
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1.获取服务器对象
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true){
            //2.获取socket对象,获取到一个,就开一条线程去执行,提高效率
            Socket socket = serverSocket.accept();
            //使用多线程提高效率
            new Thread(()->{
                try {
                    //3.根据socket获取客户端输入流
                    InputStream socketInputStream = socket.getInputStream();
                    //4.读取输入流,同时用本地输出流输出到本地文件夹中保存
                    byte[] bytes = new byte[1024];
                    int len =0;
                    //自定义命名规则,防止重名
                    String fileName="laowang"+System.currentTimeMillis()+ new Random().nextInt(9999)+".jpg";
                    File file = new File("E:\\data\\project\\tcp-study\\serverImg");
                    //如果文件夹不存在,则创建
                    if (!file.exists()){
                        file.createNewFile();
                    }
                    FileOutputStream fileOutputStream = new FileOutputStream(file+File.separator+fileName);
                    while ((len=socketInputStream.read(bytes))!=-1){
                        fileOutputStream.write(bytes,0,len);
                    }

                    //5.获取socket输出流输出保存成功的信息给客户端
                    OutputStream socketOutputStream = socket.getOutputStream();
                    socketOutputStream.write(("保存成功,url为:"+file.getCanonicalPath()+fileName).getBytes());

                    //6.关闭
                    socketOutputStream.close();
                    fileOutputStream.close();
                    socketInputStream.close();
                    socket.close();
                }catch (Exception e){
                    System.out.println(e);
                }
            }).start();
        }
    }
}

基于Tcp协议B/S架构的浏览器访问图片请求小案例

/**
 *基于TCP协议的B/S架构服务端
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1.获取服务器对象
        ServerSocket serverSocket = new ServerSocket(8888);

        //2.获取客户端socket对象
        Socket socket = serverSocket.accept();

        //3.读取客户端发送过来的请求
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len =0;
        while ((len=is.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

        //关闭资源
        is.close();
        serverSocket.close();
        socket.close();
    }
}

浏览器发起请求:http://localhost:8888/tcp-study/web/1.jpg

结果:

时隔两年,我又拿起了网络编程的socket_第1张图片

可以看到,读取出来的报文是这样的,我们就可以截取圈出来的字符串,把对应的文件写如客户端IO流中,完成!

代码如下:

/**
 * 给予TCP协议的B/S架构服务端
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //1.获取服务器对象
        ServerSocket serverSocket = new ServerSocket(8888);

        while (true){
            //2.获取客户端socket对象
            Socket socket = serverSocket.accept();
            new Thread(()->{
                try {
                    //3.读取客户端发送过来的请求
                    //读取一行足,即把文件输入流,包装成换成字符流
                    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    //读取一行
                    String line = null;
                    try {
                        line = reader.readLine();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    //根据空格分割,然后去除最开始的/,结果为:tcp-study/web/1.jpg
                    String[] split = line.split(" ");
                    String url=split[1].substring(1);
                    System.out.println("访问url:"+url);

                    //用本地流读取该路径下文件,写入到客户端流
                    FileInputStream is = new FileInputStream(url);
                    byte[] bytes = new byte[1024];
                    int len=0;

                    //写入http响应头,才可以被浏览器解析
                    OutputStream os = socket.getOutputStream();
                    os.write("HTTP/1.1 200 OK\r\n".getBytes());
                    os.write("Content-Type:text/html\r\n".getBytes());
                    os.write("\r\n".getBytes());

                    while ((len=is.read(bytes))!=-1){
                        //写出去
                        os.write(bytes,0,len);
                    }

                    //关闭资源
                    socket.close();
                    os.close();
                    is.close();
                    reader.close();
                }catch (Exception e){
                    System.out.println(e);
                }
            }).start();
        }
    }
}

请求:http://localhost:8888/web/hello.html

结果:

时隔两年,我又拿起了网络编程的socket_第2张图片

你可能感兴趣的:(网络编程,java,web服务器,socket)