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

    一.  Socket简介

    1.  Socket通信模型

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

    2:Socket:基于TCP的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
  • 1.7k
  • zjmarina

    请登录之后再进行评论

    登录
    单栏布局 侧栏位置: