• 注册
  • 赞助本站

    • 微信
    • 支付宝
    • Q Q

    感谢一直支持本站的所有人!

    • 查看作者
    • 2:Socket:基于TCP的Socket通信以及使用多线程实现多客户端的通信

      一.  Socket简介

      1.  Socket通信模型

      TCP协议是面向连接、可靠的、有序的、以字节流的方式发送数据,那么我们如何使用Java来基于TCP协议实现网络通信的呢?共有两个类,客户端的Socket类,服务端的ServerSocket类,Socket的通信模型:

      上图解析:

      •  两台服务器通信,则必然一台是客户端(Server),一台是服务端(Client),首先在服务器端建立一个ServerSocket,绑定相应的端口,并且在指定的端口进行监听,等待客户端的连接

      •  客户端创建Socket并向服务端发送请求

      • 服务器收到请求并接受客户端的请求信息,接受请求后,创建连接socket,用来和客户端的socket通过InputStream和OutputStream进行通信交换

      • 结束通信,关闭客户端和服务器的socket和相关资源

      2.  socket通信实现步骤

      • 创建ServerSocket和Socket

      • 打开连接到Socket的输入/输出流

      • 按照协议对Socket进行读/写操作

      • 关闭输入输出流、关闭Socket

      3.  ServerSocket

      ServerSocket位于java.net包下 ,用于实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。 服务器套接字的实际工作由 SocketImpl 类的实例执行。应用程序可以更改创建套接字实现的套接字工厂来配置它自身,从而创建适合本地防火墙的套接字。可以使用ServerSocket(int port)构造方法来创建绑定到特定端口的服务器套接字。其他常用方法:

      • public Socket accept() throws IOException:侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞

      • public void close()throws IOException:关闭此套接字。 在 accept() 中所有当前阻塞的线程都将会抛出 SocketException,如果此套接字有一个与之关联的通道,则关闭该通道

      • public InetAddress getInetAddress():返回此服务器套接字的本地地址

      • getLocalPort public int getLocalPort():返回此套接字在其上侦听的端口

      4.  Socket

      Socket位于java.net包下 ,用于此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。 

      套接字的实际工作由 SocketImpl 类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接字

      二.  基于TCP的Socket通信服务端

      想要基于TCP进行Socket通信,服务端需要做的事情大概有以下几步

      • 创建ServerSocket对象,绑定监听端口

      • 通过accept()方法监听客户端请求

      • 连接建立后,通过输入流读取客户端发送的请求信息

      • 通过输出流向客户端发送响应信息

      • 关闭相关资源

      来看一下每一步对应的代码:

      package com.example.sockettest.server;
      
      import java.io.BufferedReader;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.InputStreamReader;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      /**
       * @Author : ZhangJia
       * @Date : 13:55
       * @Description : 
       */
      public class Server {
          public static void main(String[] args) {
              //1.创建ServerSocket对象,绑定监听端口
              try {
                  ServerSocket serverSocket = new ServerSocket(8887);
             // 2.通过accept()方法监听客户端请求
                  System.out.println("服务器即将启动,等待客户端连接");
                  Socket socket= serverSocket.accept();
             // 3. 连接建立后,通过输入流读取客户端发送的请求信息
                  InputStream inputStream = socket.getInputStream();
                  InputStreamReader reader = new InputStreamReader(inputStream);
                  BufferedReader bufferedReader = new BufferedReader(reader);
             // 4. 通过输出流向客户端发送响应信息
                  String info = "";
                  while ((info = bufferedReader.readLine()) != null) { //循环读取客户端信息
                      System.out.println("我是服务器,客户端发给我的消息是:" +info);
                  }
                  socket.shutdownInput();  //关闭输入流(close关闭的是套接字,shutdownInput是关闭此套接字的输入流 by宋光暖)
             // 5. 关闭相关资源
                  bufferedReader.close();
                  reader.close();
                  inputStream.close();
                  socket.close();
                  serverSocket.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
      }

      三.  基于TCP的Socket通信客户端

      同样的,想要基于TCP进行Socket通信,客户端需要做的事情大体步骤为:

      • 创建Socket对象,指明需要连接的服务器的地址和端口号

      • 连接建立后,通过输出流向服务器端发送请求信息

      • 通过输入流获取服务器响应的信息

      • 关闭相关资源

      再来看一下每一步对应的客户端的代码:

      package com.example.sockettest.client;
      
      
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      /**
       * @Author : ZhangJia
       * @Date : 13:55
       * @Description :
       */
      public class Client {
          public static void main(String[] args) {
      
              try {
                  //1.创建Socket对象,指定服务器地址和端口
                  Socket socket = new Socket("localhost",8887);
                  // 2.获取输出流,向服务器端发送信息
                  OutputStream outputStream = socket.getOutputStream();
                  PrintWriter printWriter = new PrintWriter(outputStream); //将输出流包装为打印流
                  printWriter.write("你好,我是ZhangJia");
                  printWriter.flush();
                  socket.shutdownOutput();//关闭输出流
                  //3.  关闭资源
                  printWriter.close();
                  outputStream.close();
                  socket.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
      }

      接下来先启动服务器端,再启动客户端,控制台输出如下:

      服务器即将启动,等待客户端连接
      我是服务器,客户端发给我的消息是:你好,我是ZhangJia

      这就简单完成了客户端和服务器端的通信

      三.  服务器端响应客户端

      在上面的例子中,服务器端仅仅是接收到了客户端的通信,并没有对其进行任何响应,接下来修改代码,实现服务器端响应客户端的功能,实现起来也非常简单,只需要给客户端获取输入流,给服务器端提供输出流发送消息即可。客户端:

      package com.example.sockettest.client;
      
      
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      /**
       * @Author : ZhangJia
       * @Date : 13:55
       * @Description :
       */
      public class Client {
          public static void main(String[] args) {
      
              try {
                  //1.创建Socket对象,指定服务器地址和端口
                  Socket  socket = new Socket("localhost", 8887);
                  // 2.获取输出流,向服务器端发送信息
                  OutputStream outputStream = socket.getOutputStream();
                  PrintWriter printWriter = new PrintWriter(outputStream); //将输出流包装为打印流
                  printWriter.write("你好,我是ZhangJia");
                  printWriter.flush();
                  socket.shutdownOutput();//关闭输出流
                  // 3. 获取输入流,读取服务器端的响应信息
                  InputStream inputStream = socket.getInputStream();
                  BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                  String info = "";
                  while ((info = bufferedReader.readLine()) != null) { //循环读取服务器信息
                      System.out.println("我是客户端,服务器发给我的消息是:" + info);
                  }
                  socket.isInputShutdown();
                  //4.  关闭资源
                  bufferedReader.close();
                  inputStream.close();
                  printWriter.close();
                  outputStream.close();
                  printWriter.close();
                  outputStream.close();
                  socket.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
      }

      服务器端:

      package com.example.sockettest.server;
      
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      /**
       * @Author : ZhangJia
       * @Date : 13:55
       * @Description : 
       */
      public class Server {
          public static void main(String[] args) {
              //1.创建ServerSocket对象,绑定监听端口
              try {
                  ServerSocket serverSocket = new ServerSocket(8887);
             // 2.通过accept()方法监听客户端请求
                  System.out.println("服务器即将启动,等待客户端连接");
                  Socket socket= serverSocket.accept();
             // 3. 连接建立后,通过输入流读取客户端发送的请求信息
                  InputStream inputStream = socket.getInputStream();
                  InputStreamReader reader = new InputStreamReader(inputStream);
                  BufferedReader bufferedReader = new BufferedReader(reader);
             // 4. 通过输出流向客户端发送响应信息
                  String info = "";
                  while ((info = bufferedReader.readLine()) != null) { //循环读取客户端信息
                      System.out.println("我是服务器,客户端发给我的消息是:" +info);
                  }
                  socket.shutdownInput();  //关闭输入流(close关闭的是套接字,shutdownInput是关闭此套接字的输入流 by宋光暖)
              // 5.  获取输出流,响应客户端的请求
                  OutputStream outputStream = socket.getOutputStream();
                  PrintWriter printWriter = new PrintWriter(outputStream); //将输出流包装为打印流
                  printWriter.write("你好,我是ZhangYi");
                  printWriter.flush();
                  socket.shutdownOutput();//关闭输出流
             //  6. 关闭相关资源
                  bufferedReader.close();
                  reader.close();
                  inputStream.close();
                  socket.close();
                  serverSocket.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
      }

      四.  使用多线程实现多客户端的通信

      上面的代码存在这样的问题,一个服务器目前只能和一个客户端通话,我们可以使用多线程来实现多客户端的通信,基本步骤如下:

      • 服务器端创建ServerSocket,循环调用accept()等待客户端连接

      • 客户端创建一个socket并请求和服务器端连接

      • 服务器端接受客户端请求,创建socket与该客户建立专线连接

      • 建立连接的两个socket在一个单独的线程上对话

      • 服务器端继续等待新的连接

      首先创建多线程类:

      package com.example.thread;
      
      import java.io.*;
      import java.net.Socket;
      
      /**
       * @Author : ZhangJia
       * @Date : 15:29
       * @Description : 
       */
      public class ServerThread extends Thread {
          //和本线程相关的Socket
          private Socket socket;
      
          public ServerThread(Socket socket) {
              this.socket = socket;
          }
      
          //线程执行的操作,响应客户端的请求
          @Override
          public void run() {
              // 3. 连接建立后,通过输入流读取客户端发送的请求信息
              InputStream inputStream = null;
              InputStreamReader inputStreamReader = null;
              BufferedReader bufferedReader = null;
              OutputStream outputStream = null;
              PrintWriter printWriter = null;
              try {
                  inputStream = socket.getInputStream();
                  inputStreamReader = new InputStreamReader(inputStream);
                  bufferedReader = new BufferedReader(inputStreamReader);
                  // 4. 通过输出流向客户端发送响应信息
                  String info = "";
                  while ((info = bufferedReader.readLine()) != null) { //循环读取客户端信息
                      System.out.println("我是服务器,客户端发给我的消息是:" + info);
                  }
                  socket.shutdownInput();  //关闭输入流(close关闭的是套接字,shutdownInput是关闭此套接字的输入流 by宋光暖)
                  // 5.  获取输出流,响应客户端的请求
                  outputStream = socket.getOutputStream();
                  printWriter = new PrintWriter(outputStream); //将输出流包装为打印流
                  printWriter.write("你好,我是ZhangYi");
                  printWriter.flush();
                  socket.shutdownOutput();//关闭输出流
      
              } catch (Exception e) {
      
              } finally {
                  //关闭相关资源
                  try {
                      printWriter.close();
                      outputStream.close();
                      bufferedReader.close();
                      inputStreamReader.close();
                      inputStream.close();
                      socket.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
      
              }
          }
      }

      接下来修改服务器端:

      package com.example.sockettest.server;
      
      import com.example.thread.ServerThread;
      
      import java.io.*;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      /**
       * @Author : ZhangJia
       * @Date : 13:55
       * @Description : 
       */
      public class Server {
          public static void main(String[] args) {
              //1.创建ServerSocket对象,绑定监听端口
              try {
                  ServerSocket serverSocket = new ServerSocket(8887);
                  int count = 0; //记录客户端的数量
                  System.out.println("服务器即将启动,等待客户端连接");
                  Socket socket = null;
                  //循环监听等待客户端的连接
                  while (true) {
                      // 通过accept()方法监听客户端请求
                      socket = serverSocket.accept();
                      // 创建一个新的线程
                      ServerThread serverThread = new ServerThread(socket);
                      // 启动线程
                      serverThread.start();
                      System.out.println("当前客户端的数量是" + ++count);
                      System.out.println("当前客户端计算机名" + socket.getInetAddress().getHostName());
                      System.out.println("当前客户端IP" + socket.getInetAddress().getHostAddress());
                      System.out.println("-------------------");
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
      }

      此时启动服务器端,再启动多次客户端,控制台输出如下:

      服务器即将启动,等待客户端连接
      当前客户端的数量是1
      我是服务器,客户端发给我的消息是:你好,我是ZhangJia
      当前客户端计算机名127.0.0.1
      当前客户端IP127.0.0.1
      -------------------
      当前客户端的数量是2
      当前客户端计算机名127.0.0.1
      我是服务器,客户端发给我的消息是:你好,我是ZhangJia
      当前客户端IP127.0.0.1
      -------------------

      参考资料

      本文所有内容根据慕课网汤小洋老师:Java Socket应用---通信是这样练成的 教程整理而成

    • 0
    • 0
    • 0
    • 120