java - 多线程服务器

标签 java multithreading sockets data-structures

我认为我没有完全理解让多个客户端连接到一台服务器的概念。 我见过它以多种不同的方式完成,听说过它如何以多种不同的方式工作..

据我了解,每次 ServerSocket 从客户端套接字获取连接时,它都会创建一个新的 Socket,以便它可以继续监听。

当我看到人们用代码(服务器端)编写它时,他们总是使用一个套接字。 从那时起我就一直这样做,但仍然没有取得任何进展。

我的 friend 编写了客户端,它与服务器一起工作,但是我们在让服务器全局显示消息方面遇到了问题。这就是我的结构(前 3 个用于服务器,最后一个用于客户端:

服务器.java

package Main;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import Streams.Stream;

public class Server {

    public static final int maxConnections = 10;

    ServerSocket serverSocket;
    Socket socket;

    User[] users = new User[maxConnections];

    public Server() {
        try {
            serverSocket = new ServerSocket(43594);

            while(Stream.streams < maxConnections) {
                socket = serverSocket.accept();

                for(User user : users) {
                    if(user == null) {
                        user = new User(socket);

                        Thread t = new Thread(user);
                        t.start();
                        System.out.println("Someone has joined the chat!");
                        return;
                    }
                }

            }


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

    public static void main(String[] args) {
        new Server();
    }
}

用户.java

package Main;

import java.io.IOException;
import java.net.Socket;

import Streams.Stream;

public class User implements Runnable {

    Stream stream;

    public User(Socket socket) {
        stream = new Stream(socket);

    }

    public void run() {
        String textInput, textOutput;

        while(stream.exists()) {
            try{
                textInput = (String) stream.recieveData();
            }catch(IOException  e) {
                e.printStackTrace();
            }catch(ClassNotFoundException e) { e.printStackTrace(); }
        }

    }

    public void sendMessage(String message) throws IOException {
        stream.sendData(message);
    }

}

流.java

package Streams;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class Stream {
    public static int streams = 0;

    Socket socket;

    ObjectInputStream input; ObjectOutputStream output;
    Object data;

    public Stream(Socket userSocket) {
        streams++;
        socket = userSocket;

        try{
            input = new ObjectInputStream(userSocket.getInputStream());
            output = new ObjectOutputStream(userSocket.getOutputStream());
        }catch(IOException e) { e.printStackTrace(); }

    }

    public void sendData(Object data) throws IOException {
        output.writeObject(data);
        output.flush();
    }

    public Object recieveData() throws IOException, ClassNotFoundException {
        return data = input.readObject();
    }


    public boolean exists() {
        if(socket.isClosed()) return false; else return true;
    }

}

客户端.java

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class Client extends JFrame {

    private JTextField userText;
    private JTextArea chatWindow;
    private ObjectOutputStream out;
    private ObjectInputStream in;
    private String message = "";
    private String serverIP;
    private Socket clientSocket;
    private int port = 43594;
    Boolean CNC = false;

    //constructor
    public Client(String serverIP) {
        super("Client Chat");

        this.serverIP = serverIP;

        userText = new JTextField();
        userText.setEditable(false);
        userText.addActionListener(new ActionListener(){
                public void actionPerformed(ActionEvent e) {
                    if(userText.getText().length() > 0) {
                        sendMessageToServer(userText.getText());
                        userText.setText("");
                    }
                }
            }
        );
        add(new ClientMenu(), BorderLayout.NORTH);
        add(userText, BorderLayout.SOUTH);
        chatWindow = new JTextArea();
        chatWindow.setEditable(false);
        add(new JScrollPane(chatWindow), BorderLayout.CENTER);
        add(new JScrollPane(new ClientTable()), BorderLayout.EAST);
        setSize(600, 300);
        setVisible(true);
    }

    //connect to server
    public void startRunning() {
        try{
            connectToServer();
            setupStreams();
            whileChatting();
        } catch(EOFException eofException) {
            showMessage("\n Client terminated the connetion");
        }catch(IOException ioException) {
            ioException.printStackTrace();
        }finally{
            closeCrap();
        }
    }

    //connect to server
    private void connectToServer() {
        showMessage("Attempting to connect to server... \n");
        try {
            clientSocket = new Socket(serverIP, port);
        } catch (UnknownHostException e) {
            CNC = true;
            e.printStackTrace();
        } catch (IOException e) {
            CNC = true;
            e.printStackTrace();
        }
        //showMessage("Connected to:" + connection.getInetAddress().getHostName());
    }

    //setup streams to send and receive messages
    private void setupStreams() {
        try {
            out = new ObjectOutputStream(clientSocket.getOutputStream());
            out.flush();
            in = new ObjectInputStream(clientSocket.getInputStream());
            showMessage("Stream established! \n");
            showMessage("Use ::setname to change your name \n");
        }catch(IOException e) { e.printStackTrace(); }

    }

    //while chatting with server
    private void whileChatting() throws IOException{
        ableToType(true);
        do{
            try{
                message = (String) in.readObject();

                showMessage("\n" + message);
            }catch(ClassNotFoundException classNotfoundException) {
                showMessage("\n ERROR! Message cannot be read");
            }
        }while(!message.equals("SERVER - END"));
    }

    //close the streams and sockets
    private void closeCrap(){
        if(CNC) {
            showMessage("ERROR! Could not connect to server");
        } else {
        showMessage("\n Ending connections...");
        ableToType(false);
        try{
            out.close();
            in.close();
            clientSocket.close();
        }catch(IOException ioException) {
            ioException.printStackTrace();
        }
      }
    }

    private void sendMessageToServer(String message) {
        try{
            out.writeObject(message);
            out.flush();
        }catch(IOException ioException) {
            chatWindow.append("\n ERROR! Could not send message!");
        }
    }

    //change/update chatWindow
    private void showMessage(final String message) {
        SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                        chatWindow.append(message);
                    }
                }
            );
    }

    //gives user permission to enter messages into text box
    private void ableToType(final boolean tof) {
        SwingUtilities.invokeLater(
                new Runnable() {
                    public void run() {
                        userText.setEditable(tof);
                    }
                }
            );
    }

    public static void main(String[] args) {
        Client c = new Client("thisisatestip.zapto.org");
        c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        c.setLocationRelativeTo(null);
        c.startRunning();
    }
}

我在想,不要将 ServerSocket 设为静态,而是调用 User 的构造函数,例如

User(new Socket())

并接受 User 类中的连接。 请告诉我

最佳答案

首先,我已经很长时间没有使用 Java 了,只是用 C# 编写了一个简单的客户端/服务器聊天程序。希望这些概念仍然相同。

关于您的服务器类,我注意到一些事情:

  1. 您从构造函数“返回”。不确定这在 Java 中是否合法(或者是一个好主意),但从它的外观来看,一旦客户端连接,它就会关闭服务器。例如:客户端建立连接,服务器将“user”分配给数组,服务器从构造函数返回,main 退出,程序终止。我建议让你的构造函数完成所有设置,然后使用一个无限循环并添加新客户端的“start”方法。在 native 代码 (C/C++) 中,“accept()” block - 我不确定 Java 中是否是这种情况,但您可以在这里利用它来发挥您的优势。鉴于此,您还可以使用动态大小的数组(我认为是 ArrayList?),因此您不限于 10 个客户端。因此,获取连接,使用 Socket 创建用户对象,让该用户做它的事情,循环并等待另一个客户端连接。

  2. 您为每个用户对象创建一个线程。这可能不是一个好主意,因为大量并发线程带来的开销将大大降低服务器的性能。我在聊天实现中所做的就是创建一个 threadpool在服务器上用于从客户端接收数据,并在每次客户端向服务器发送数据并且服务器接收到数据时创建一个工作线程。我在服务器端使用了回调函数(不确定 Java 中是否有类似的功能)和非阻塞操作,这样我就不必为每个客户端提供专用线程。

  3. 鉴于您正在使用线程,您需要 lock围绕重要数据。锁通过防止两个(或更多)线程同时尝试访问资源来序列化对资源的访问。

对于你原来的问题,我建议如下:接受服务器端的连接,从中获取为客户端创建的套接字,并将其传递给用户对象。

关于java - 多线程服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18661872/

相关文章:

java - 当我想要上一级目录时,ServletContext 没有给我真正的路径

Android SurfaceView Canvas 用线程绘制

multithreading - 分析死锁时"DebugInfo for CritSec does not point back to the critical section"

c - 如何在不读取数据的情况下监听socket?

Java 集合与手动循环 - 编码挑战/面试最佳实践

java - 在 Java 中尝试 Catch block

sockets - 在后台运行服务器,而应用程序执行其他操作

java - Kotlin上的Server-Client应用程序出现问题

java - 反转句子中的单词

python - selenium python 中的多线程