0

我在这里做了很多搜索,但我没有找到任何与我需要的东西相匹配的东西。

我在客户端/服务器应用程序方面不是那么专家,这是我的第一次尝试,所以,如果我犯了一些错误或者我提出了愚蠢的问题,请耐心等待。

现在,回到我的问题..

我需要构建一个多客户端/服务器应用程序。

服务器端应该是客户端的简单管理器。

因此,例如:

  • 服务器可以更改一些客户端参数
  • 接受来自客户端的文件
  • 其他无聊的东西..基本上可以翻译成发送字符串或发送文件或获取字符串和文件

另一方面,客户端是一个复杂的应用程序(至少对于用户而言),它应该发送到服务器:

  • 一些用户注册数据(这里不需要安全的东西,我已经对一个简单的客户端/服务器连接搞砸了)
  • 一个 PSD 文件,应该是用户工作的结果,所以当用户点击“我完成了”时,应用程序会获取这个 PSD 文件并将其发送到服务器用于存储目的
  • 其他客户端信息,例如我们的默认配置数据,等等..

所以,我的问题基本上是这样的:如何处理来自服务器与一个特定客户端的通信?我的意思是,我已经启动了服务器,并且我只为一个客户端更改配置。

我想我需要以某种方式存储客户端......就像在数组(a List)中,但我不知道这是否是正确的方法。(基本上我不知道课程SocketServerSocket工作原理。如果这可以帮助您更好地理解)

此外,当服务器启动并监听时.. GUI 需要更新以显示新连接的客户端,所以我需要某种服务器监听器,当新客户端出现时将操作返回到界面?(很多人使用 while(true) { socket = server.accept(); } 方法,但这对我来说听起来不是很聪明..)

这是基本的 Client.java 和 Server.java 文件,其中包含我根据大量 Google 搜索编写的客户端和服务器基本功能。

但是下面的所有代码都不能算出我所有的需求..

客户端.java

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client extends Socket {

    private static Client    instance    = null;

    /**
     * The main init() function for this class, to create a Singleton instance for the Client
     * 
     * @param host
     *            The host of the Server
     * @param port
     *            The port of the Server
     * @return The Client instance that is a new instance if no one exists previusly,
     *         otherwise an older instance is returned
     * @throws UnknownHostException
     * @throws IOException
     */
    public static Client init( String host, Integer port ) throws UnknownHostException, IOException
    {
        if ( Client.instance == null )
            Client.instance = new Client( host, port );
        return Client.instance;
    }

    /**
     * Default Constructor made private so this class can only be instantiated by the
     * singleton init() function.
     * 
     * @param host
     *            The host of the server
     * @param port
     *            The port of the server
     * @throws UnknownHostException
     * @throws IOException
     */
    private Client( String host, Integer port ) throws UnknownHostException, IOException
    {
        super( host, port );
    }

    /**
     * Function used to send a file to the server.
     * When this function fires, the Client class start sending a file to the server.
     * Internally this function handles the filesize, and some other file information
     * that the server needs to store the file in the correct location
     * 
     * @param filename
     *            The filename of the file that will be sended to the server
     */
    public void sendFile( String filename ) throws FileNotFoundException, IOException
    {
        // The file object from the filename
        File file = new File( filename );

        // A string object to build an half of the message that will be sent to the exceptions
        StringBuilder exception_message = new StringBuilder();
        exception_message.append( "The File [" ).append( filename ).append( "] " );

        // Check if the file exists
        if ( !file.exists() )
            throw new FileNotFoundException( exception_message + "does not exists." );

        // Check if the file size is not empty
        if ( file.length() <= 0 )
            throw new IOException( exception_message + "has zero size." );

        // Save the filesize
        Long file_size = file.length();

        // Check if the filesize is something reasonable
        if ( file_size > Integer.MAX_VALUE )
            throw new IOException( exception_message + "is too big to be sent." );

        byte[] bytes = new byte[file_size.intValue()];

        FileInputStream fis = new FileInputStream( file );
        BufferedInputStream bis = new BufferedInputStream( fis );
        BufferedOutputStream bos = new BufferedOutputStream( this.getOutputStream() );

        int count;

        // Loop used to send the file in bytes group
        while ( ( count = bis.read( bytes ) ) > 0 )
        {
            bos.write( bytes, 0, count );
        }

        bos.flush();
        bos.close();
        fis.close();
        bis.close();
    }

    /**
     * Function used to send string message from client to the server
     * 
     * @param message
     *            The string message the server should get
     * @throws IOException
     */
    public void sendMessage( String message ) throws IOException
    {
        OutputStream os = this.getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter( os );
        BufferedWriter bw = new BufferedWriter( osw );

        bw.write( message );
        bw.flush();
    }

    /**
     * Function used to get a message from the Server
     * 
     * @return The message the server sent back
     * @throws IOException
     */
    public String getMessage() throws IOException
    {
        InputStream is = this.getInputStream();
        InputStreamReader isr = new InputStreamReader( is );
        BufferedReader br = new BufferedReader( isr );

        String message = br.readLine();

        return message;
    }
}

服务器.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class Server extends ServerSocket {

    private static Server    instance    = null;
    private Socket            socket        = null;

    /**
     * 
     * @param port
     * @return
     * @throws IOException
     */
    public static Server init( Integer port ) throws IOException
    {
        if ( Server.instance == null )
            Server.instance = new Server( port );
        return Server.instance;
    }

    /**
     * 
     * @param port
     * @throws IOException
     */
    private Server( Integer port ) throws IOException
    {
        super( port );

        // Maybe this is something that needs to be improved
        while ( true )
            this.socket = this.accept();
    }

    /**
     * 
     * @param message
     * @throws IOException
     */
    public void sendMessage( String message ) throws IOException
    {
        OutputStream os = this.socket.getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter( os );
        BufferedWriter bw = new BufferedWriter( osw );

        bw.write( message );

        bw.flush();
    }

    /**
     * 
     * @return
     * @throws IOException
     */
    public String getMessage() throws IOException
    {
        InputStream is = this.socket.getInputStream();
        InputStreamReader isr = new InputStreamReader( is );
        BufferedReader br = new BufferedReader( isr );

        String message = br.readLine();

        return message;
    }
}

嗯..为我的英语道歉..拜托。

4

2 回答 2

2

你的问题让我很好奇,现代 Java 方法会是什么样子。当我开始尝试使用套接字时,我也遇到了一些问题,所以这里有一个小例子可以帮助你。

服务器在自己的“线程”中处理每个客户端,您可以说这是基本的客户端/服务器架构。但我使用新的Callable<V>而不是线程。

我没有扩展Socket也没有ServerSocket。我以前很紧张地看到过。我认为在这种情况下更倾向于组合而不是继承。它为您提供了更多控制权,因为您可以委派您喜欢的内容和方式。

有关更多信息,我建议您查看oracle 教程

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ClientServerExample
{
  private final static int PORT = 1337;
  private final static String LOOPBACK = "127.0.0.1";

  public static void main(String[] args) throws IOException
  {
    ExecutorService se = Executors.newSingleThreadExecutor();
    se.submit(new Server(PORT, 5));

    ExecutorService ce = Executors.newFixedThreadPool(3);
    for (String name : Arrays.asList("Anton", "John", "Lisa", "Ben", "Sam", "Anne"))
      ce.submit(new Client(name, LOOPBACK, PORT));

    ce.shutdown(); while (!ce.isTerminated()) {/* wait */}
    se.shutdown();
  }
}

class Client implements Callable<Void>
{
  private final String name;
  private final String ip;
  private final int port;

  public Client(String name, String ip, int port)
  {
    this.name = name;
    this.ip = ip;
    this.port = port;
  }

  @Override
  public Void call() throws Exception
  {
    Socket s = new Socket(ip, port);
    PrintWriter out = new PrintWriter(s.getOutputStream(), true);
    out.println("Hi, I'm " + name + "!");
    out.close();
    s.close();
    return null;
  }
}

class Server implements Callable<Void>
{
  private final int port;
  private final int clients;
  private final ExecutorService e;

  public Server(int port, int clients)
  {
    this.port = port;
    this.clients = clients;
    this.e = Executors.newFixedThreadPool(clients);
  }

  @Override
  public Void call() throws Exception
  {
    ServerSocket ss = new ServerSocket(port);
    int client = 0;
    while (client < clients)
    {
      e.submit(new ClientHandler(client++, ss.accept()));
    }
    ss.close();
    e.shutdown(); while (!e.isTerminated()) {/* wait */}
    return null;
  }
}

class ClientHandler implements Callable<Void>
{

  private int client;
  private Socket s;

  public ClientHandler(int client, Socket s)
  {
    this.client = client;
    this.s = s;
  }

  @Override
  public Void call() throws Exception
  {
    BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
    String fromClient;
    while ((fromClient = in.readLine()) != null)
    {
      System.out.println("FROM CLIENT#" + client + ": " + fromClient);
    }
    in.close();
    s.close();
    return null;
  }
}

输出

来自客户#0:嗨,我是约翰!
来自客户#2:嗨,我是山姆!
来自客户#1:嗨,我是 Ben!
来自客户#3:嗨,我是安妮!
来自客户#4:嗨,我是安东!

于 2013-09-05T14:21:02.507 回答
1

这是直到工作正在进行中,请耐心等待......最初只是解决我已经在评论中发布的关于可能模型的内容。今晚晚些时候会有更新。

不同型号

通常想到的是这三种方法:

  • 多进程模型
  • 多线程模型
  • 事件循环模型

但是,这些并不局限于网络编程的客户端-服务器模型,而且在其他场景中也很常见,比如在 GUI 编程中。

多进程模型

//传入

多线程模型

//传入

事件循环模型

事件循环是程序等待传入指令将消息分发到系统其他部分的结构。它的主要优点在于其实现的简单性和轻量级的方面。

如果您的主程序不执行任何计算密集型和长时间的操作,则它特别有用,以免阻塞下一个传入连接太久。这个想法很简单,等待,建立连接,分派到一个单独的子系统做某事,然后等待答案,直到你能做出回应。

您可以在响应之前等待子系统的回答(同步),或者开始处理其他连接,直到子系统返回并且您可以响应(异步)。


更新

根据您最近的评论,在我看来,您受到了一些严格的时间限制,也许您应该稍微推迟学习经验并优先考虑发展速度。出于这个原因,我建议您考虑使用一个框架,该框架可以让您覆盖您的基础并为您处理杂乱无章的事情 - 我最初认为您是作为个人努力的一部分提出这个问题并尝试学习。我会重新确定优先级并专注于完成工作,即使最初的理解可能会受到影响。

所以...根据我们讨论的内容,我建议您转向netty.io一个事件驱动的网络编程框架(参见wiki示例)。

于 2013-09-05T15:08:52.857 回答