多线程服务器

时间:2013-09-06 15:59:28

标签: java multithreading sockets data-structures

我不认为我完全理解让多个客户端连接到一台服务器的概念。 我已经看到它做了很多不同的方式,听说它以多种不同的方式起作用。

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

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

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

Server.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();
    }
}

User.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);
    }

}

Stream.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;
    }

}

Client.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类中的连接。 请让我知道

1 个答案:

答案 0 :(得分:2)

让我先说明我在很长一段时间内没有使用过Java,但我只是用C#编写了一个简单的客户端/服务器聊天程序。希望这些概念都是一样的。

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

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

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

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

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