zoukankan      html  css  js  c++  java
  • java socket通讯

    本来是打算验证java socket是不是单线程操作,也就是一次只能处理一个请求,处理完之后才能继续处理下一个请求。但是在其中又发现了许多问题,在编程的时候需要十分注意,今天就拿出来跟大家分享一下。

    首先先建立一个服务端代码,运行时也要先启动此程序。

    package com.test.some.Socket;
    
    import java.io.*;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /**
    * @Description: socket服务端代码
    * @Author:      haoqiangwang3
    * @CreateDate:  2020/1/9
    */
    public class MySocketServer1 {
        // 服务器监听端口
        private static int port = 8081;
    
        public static void main(String[] args) throws InterruptedException {
            try {
                //1.得到一个socket服务端
                ServerSocket serverSocket = new ServerSocket(port);
                while (true) {
    
                    // 2.等待socket客户端的请求。accept方法在有连接请求时才会返回
                    System.out.println("等待客户端请求。。。");
                    Socket socket = serverSocket.accept();
                    System.out.println("客户端请求来了。。。");
    
                    // 3.获取socket输入流
                    InputStream inputStream = socket.getInputStream();
                   /* BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                    System.out.println("接收到的请求数据为:" + bufferedReader.readLine());*/
                    // 读取请求内容的缓冲区
                    byte[] bytes = new byte[1024];
                    int length = 0;
                    StringBuilder sb = new StringBuilder();
                    //获取客户端请求的内容
                    while ((length = inputStream.read(bytes)) != -1) {
                        sb.append(new String(bytes, 0, length, "utf-8"));
                    }
                    System.out.println("接收到的请求数据为:" + sb.toString());          
              //Thread.sleep(50000);

              // 4.获取socket输出流 OutputStream outputStream = socket.getOutputStream(); PrintWriter printWriter = new PrintWriter(outputStream); String backStr = "服务端接收到了请求"; printWriter.write(new String(backStr.getBytes(), "utf-8")); printWriter.flush(); //5.关闭资源 //bufferedReader.close(); inputStream.close(); printWriter.close(); outputStream.close(); socket.close(); } } catch (IOException e) { System.err.println("socket监听失败!" + e); } } }

    此代码模拟了正常系统成socket服务端的方式,就是一个无限循环监听我们绑定的端口,当有客户端请求来了之后进行处理。

    下面就是客户端请求代码

    package com.test.some.Socket;
    
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    
    /**
    * @Description: socket客户端代码
    * @Author:      haoqiangwang3
    * @CreateDate:  2020/1/9
    */
    public class MySocketClient1 {
        //socket请求ip地址
        private static String host = "127.0.0.1";
    
        //socket请求端口
        private static int port = 8081;
    
        public static void main(String[] args) {
            try {
                //1.建立一个客户端
                Socket socket = new Socket(host, port);
    
                //2.得到socket输出流
                OutputStream outputStream = socket.getOutputStream();
                PrintWriter printWriter = new PrintWriter(outputStream);
                String sendStr = "发送数据1";
                //发送数据
                printWriter.write(sendStr);
                printWriter.flush();
                socket.shutdownOutput();
    
                //3.得到socket输入流
                InputStream inputStream = socket.getInputStream();
                StringBuilder sb = new StringBuilder();
                byte[] bytes = new byte[1024];
                while (inputStream.read(bytes) != -1) {
                    sb.append(new String(bytes, "utf-8"));
                }
                System.out.println("接收到的返回数据为:" + sb);
    
                //4.关闭资源
                printWriter.close();
                outputStream.close();
                inputStream.close();
                socket.close();
            } catch (Exception e) {
                System.err.println("socket请求失败" + e);
            }
        }
    
    }

    客户端代码主要就是向服务端发送数据,然后等待服务端的响应,打印出服务端的响应内容。

    最终打印结果如下。服务端:

     客户端:


    首先明确几个概念,下面将会用到。

    flush()方法:用于清空缓冲区的数据流,进行流的操作时,数据先被读到内存缓冲区中,然后再用数据写到文件中。

    socket.shutdownOutput()方法:他是一种单向关闭流的方法,即关闭客户端的输出流并不会关闭服务端的输出流。通过shutdownOutput()方法只是关闭了输出流,但socket仍然是连接状态,连接并未关闭。

    printWriter.close()方法:如果直接关闭输入或者输出流,即:in.close()或者out.close(),会直接关闭socket。

    流中的关闭顺序:一般情况下是:先打开的后关闭,后打开的先关闭。另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b,例如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b。当然完全可以只关闭处理流,不用关闭节点流。处理流关闭的时候,会调用其处理的节点流的关闭方法。如果将节点流关闭以后再关闭处理流,会抛出IO异常。

    下面总结下我遇到的问题。

    1.客户端发送数据部分的代码,printWriter.flush(); socket.shutdownOutput(); 这两句代码十分的重要,flush()方法如果不添加的话,服务端接收到的数据将为空,shutdownOutput()方法不添加的话,服务端将一直等待读取客户端的数据,不会往下进行,大家可以自测一下。我自己的理解是flush()的作用是为了把数据从内存中刷新到socket流中,shutdownOutput()方法是告诉服务端,我没有东西要传输了,所以服务端也就会停止等待读取客户端发送的内容,程序就可以继续向下走。

    2.打开服务端中的sleep方法,在新建一个客户端,同时开启请求服务端,会发现服务端确实是一个连接一个连接的处理,所以这也是socket性能所在的问题。

    3.如果不用字符流读取,客户端发送数据直接用outputStream.write(sendStr.getBytes());,可以发现此时不用调用flush()方法,但是socket.shutdownOutput()依然需要。这是因为直接读取到socket的输出流,并没有读到内存中。

  • 相关阅读:
    [灵魂拷问]MySQL面试高频100问(工程师方向)
    前后端分离模式下的权限设计方案
    Netty实战:设计一个IM框架
    超实用,Linux中查看文本的小技巧
    Java面试,如何在短时间内做突击
    挑战10个最难回答的Java面试题(附答案)
    SpringBoot是如何动起来的
    Lab_2_SysOps_VPC_Linux_v2.5
    Lab_1_SysOps_Compute_Linux_v2.5
    change-resource-tags.sh
  • 原文地址:https://www.cnblogs.com/wanghq1994/p/12170711.html
Copyright © 2011-2022 走看看