0

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

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

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

我的朋友编写了客户端,它与服务器一起工作,但我们在让服务器全局显示消息时遇到了问题。这就是我的结构(前 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 类中的连接。请告诉我

4

1 回答 1

2

让我以我很久没有在 Java 中工作过,但我只是用 C# 编写了一个简单的客户端/服务器聊天程序来作为开头。希望这些概念仍然相同。

关于您的 Server 类,我注意到了一些事情:

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

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

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

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

于 2013-09-06T16:32:51.660 回答