JAVA实现SOCKET多客户端通信

  • 一、ServerSocket
  • 1.为了方便调试,先创建一个界面用于显示客户端连接信息
  • 2.启动Socket服务
  • 3.服务器处理类HandleServer
  • 4.一些方法方便ServerTools类实现
  • 二、ServerTools
  • 1.实现指定服务器ID输出信息的工具
  • 三、ClientSocket
  • 1.同样的先建一个简单的界面用于输出信息和显示信息
  • 2.创建一个线程用于循环获取信息并显示
  • 3.新建一个TestClient类 这个类 和ClientSocket 一模一样 就是拿来测试的
  • 四、总结


一、ServerSocket

1.为了方便调试,先创建一个界面用于显示客户端连接信息

java socket大小端 java socket客户端_java socket大小端


基于javafx包写的一个简单界面!

javafx.scene.control.TextArea  ta = new javafx.scene.control.TextArea();
    @Override
    public void start(Stage primaryStage) throws Exception {
        scene = new Scene(ta,450,200);
        primaryStage.setTitle("SocketServer");
        primaryStage.setScene(scene);
        primaryStage.show();
        pStage = primaryStage;
        new Thread(new MyServer()).start();  //创建线程启动Socket服务
        }

2.启动Socket服务

public class MyServer implements Runnable{
        @Override
        public void run() {
            try{
                java.net.ServerSocket serverSocket = new java.net.ServerSocket(8000);
                ta.appendText("Server started at " + new Date()+"\n");
                while(true){
                    Socket socket = serverSocket.accept(); //程序会在这里阻塞,直到等到客户端连接
                    clientNumber++;
                    /*
                    这里就是在界面中输出一些服务器、和连接的客户端信息
                     */
                    Platform.runLater(()->{
                        ta.appendText("Starting thread for client " + clientNumber + " at " +
                                new Date() +"\n");
                        InetAddress inetAddress = socket.getInetAddress();
                        ta.appendText("Client "+clientNumber + "'s host name is" +inetAddress.getHostName()
                        +"\n");
                        ta.appendText("Client"+clientNumber + "'s IP address is "+ inetAddress.getHostAddress()+"\n");
                    });
                    /*
                    每有一个客户端连接服务器就创建一个线程,进行通信处理
                     */
                    new Thread(new HandleServer(socket)).start();
                    try{
                        Thread.sleep(100);   //多个客户端连续快速连接服务器时,可能出现问题,这里设置延时
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }

            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

这一段代码主要作用就是循环等待客户端连接服务器:
Socket socket = serverSocket.accept();
在写这篇博客时,突然想知道阻塞的原理就去查了一下。。。。
然而并没有看懂。。这个应该涉及到操作系统层面,等之后把操作系统搞明白了在来补充吧。

3.服务器处理类HandleServer

class HandleServer implements Runnable {
        private Socket socket;
        private int name;
        private int toClientID;
        private DataOutputStream outputStream;
        private DataInputStream inputStream;
        public HandleServer(Socket socket){
            this.socket = socket;
            ServerTools.Tools().add(this);
            this.name = clientNumber;
        }
        @Override
        public void run() {
            try{
                inputStream = new DataInputStream(socket.getInputStream());
                outputStream = new DataOutputStream(socket.getOutputStream());
                outputStream.writeUTF("Your ID is:"+clientNumber);
                while (true){
                    toClientID = inputStream.readInt();
                    String messageGET = inputStream.readUTF();
                    int err = ServerTools.Tools().MyWriteUTF(messageGET,toClientID); //MyWriteUTF 是一个自定义方法,serverTools.Tools()是一个工具类,一个静态对象。
                    if (err==0){
                        outputStream.writeUTF("No have this ID!");
                    }
                    Platform.runLater(()->{
                        ta.appendText(socket.getInetAddress().getHostName()+" Message received from client:" + messageGET +"\n" );
                    });
                    System.out.println(clientNumber);
                }
            }catch (IOException e){
                clientNumber--;
                System.out.println(clientNumber);
                System.err.println("Client is closed!");
            }

        }

这一块的代码主要就是创建输入输出数据流了
inputStream = new DataInputStream(socket.getInputStream());
outputStream = new DataOutputStream(socket.getOutputStream());

4.一些方法方便ServerTools类实现

public void MyWriteUTF(String message){
            try {
                outputStream.writeUTF(message);
            } catch (IOException e) {
                ServerTools.Tools().remove(this);
                e.printStackTrace();
            }
        }

        public int getName() {
            return name;
        }

二、ServerTools

1.实现指定服务器ID输出信息的工具

public class ServerTools {

    private static final ServerTools servertools = new ServerTools();
    public static ServerTools Tools(){
        return  servertools;
    }

    Vector<MyServerSocket.HandleServer> vector = new Vector<MyServerSocket.HandleServer>();
    public void add(MyServerSocket.HandleServer cs){
        vector.add(cs);
    }
    public void remove(MyServerSocket.HandleServer cs){
        vector.remove(cs);
    }

    public int MyWriteUTF(String message,int target) {
        for (int i = 0; i <= target; i++){
            try {
                if (vector.get(i).getName() == target) {
                    MyServerSocket.HandleServer MSSHC = vector.get(i);
                    MSSHC.MyWriteUTF(message);
                    return 1;
                }
            }catch (ArrayIndexOutOfBoundsException e){
                e.printStackTrace();
                return 0;
            }
        }
        return 0;

    }
}

vector用于保存客户端连接信息
一个粗糙的处理方式,逻辑上缺陷还很严重,主要我好像没找到这样的框架???
缺陷:因为服务器要返回客户端的ID让客户端将ID显示到交互界面,所以存在情况客户端多次连接断开后会使返回的ID出现重复

三、ClientSocket

1.同样的先建一个简单的界面用于输出信息和显示信息

java socket大小端 java socket客户端_Server_02


第一个编辑框就是 输入要发送指定客户端的ID 例如:1 或 2 这样的???

第二个编辑框就是 输入你要发送的信息了,很清楚

下面的就是显示框,嗯!

public class MyClientSocket extends Application {

    private Socket socket;
    private DataOutputStream toServer = null;
    private DataInputStream fromServer = null;
    private String ID;
    private int targetID = 0;
    private TextArea ta;

    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane paneForTextField = new BorderPane();
        paneForTextField.setPadding(new Insets(5,5,5,5));
        paneForTextField.setStyle("-fx-border-color: green");
        paneForTextField.setLeft(new Label("Enter a Message:"));

        TextField tf = new TextField();
        tf.setAlignment(Pos.BOTTOM_RIGHT);
        paneForTextField.setCenter(tf);

        BorderPane ID_lable = new BorderPane();
        ID_lable.setPadding(new Insets(5,5,5,5));
        ID_lable.setStyle("-fx-border-color: green");
        ID_lable.setLeft(new Label("Enter a ID for send message:"));

        TextField getId = new TextField();
        getId.setAlignment(Pos.BOTTOM_RIGHT);
        ID_lable.setCenter(getId);
        paneForTextField.setTop(ID_lable);


        BorderPane mainPane = new BorderPane();
        ta = new TextArea();
        mainPane.setCenter(new ScrollPane(ta));
        mainPane.setTop(paneForTextField);


        Scene scene = new Scene(mainPane,450,200);
        primaryStage.setTitle("SocketClient");
        primaryStage.setScene(scene);
        primaryStage.show();

        tf.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                targetID = Integer.parseInt(getId.getText().trim());
                if (targetID > 0 || targetID!=Integer.parseInt(ID));
                else return;
                try {
                    String putMessage = tf.getText().trim();
                    toServer.writeInt(targetID);
                    toServer.writeUTF(putMessage);
                    toServer.flush();
                    ta.appendText("PUT message is :"+ putMessage +"\n");
                    tf.setText("");
                }catch (IOException ex ){
                    System.err.println(ex);
                }
            }
        });

        try{
            socket = new Socket("localhost",8000);
            fromServer = new DataInputStream(socket.getInputStream());
            toServer = new DataOutputStream(socket.getOutputStream());
            ID = fromServer.readUTF();
            paneForTextField.setRight(new Label("Your ID is:"+ID));
            new Thread(new getMessage(socket,fromServer)).start();
        }catch (IOException ex){
            ta.appendText(ex.toString() +"\n");
        }
    }
}

一样的要new一个Socket 去连接服务器,socket(),括号里的就是服务器的IP,和程序的端口号了,这种基于tcp协议的好像都是一个样???

2.创建一个线程用于循环获取信息并显示

class getMessage implements Runnable{
        private Socket socket;
        private DataInputStream formServer;
        public getMessage(Socket socket,DataInputStream formServer){
            this.socket = socket;
            this.formServer = formServer;
        }
        @Override
        public void run() {
            try {
                while (true) {
                    String Message = formServer.readUTF();
                    try{
                        Thread.sleep(100);
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ta.appendText("GET message from server is:" + Message + "\n");

                }
            }catch (IOException e){
                System.err.println(e);
            }

        }
    }

很简单了,依旧是输入输出数据流,然后循环等待信息并输出。

3.新建一个TestClient类 这个类 和ClientSocket 一模一样 就是拿来测试的

java socket大小端 java socket客户端_服务器_03

四、总结

java写socket 是真的简单!!!^_ ^!

java socket大小端 java socket客户端_java socket大小端_04