如何处理用于发送消息和/或文件的客户端/服务器应用程序

时间:2013-09-05 13:12:30

标签: java sockets client-server serversocket

我在这里做了很多搜索,但我发现任何与我需要的东西相符的东西。

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

现在,回到我的问题..

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

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

所以,例如:

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

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

  • 一些用户注册数据(此处不需要安全资料,我已经非常混淆了简单的客户端/服务器连接)
  • 一个PSD文件应该是用户工作的结果,所以当用户点击"我已完成"应用程序获取此PSD文件并将其发送到服务器以进行存储
  • 其他客户信息,例如我们的默认配置数据等等。

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

我想我需要以某种方式存储客户端...比如数组(List),但我不知道这是否是正确的方法。 (基本上我不知道班级SocketServerSocket如何运作......如果这可以帮助你更好地理解)

此外,当服务器启动并监听时......需要更新GUI以显示新连接的客户端,因此当需要新客户端显示时,我需要某种服务器监听器将该操作发送回接口吗? (很多人都使用     while(true){         socket = server.accept();     } 方法,但这对我来说听起来不太聪明..)

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

但是下面的所有代码并没有计算我所有的需求..

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

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

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

2 个答案:

答案 0 :(得分:1)

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

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

我没有延长Socket也不延长ServerSocket。我以前看过那个。我认为在这种情况下,优于组合优于继承更好。它可以为您提供更多控制,因为您可以委派您喜欢的内容和方式。

有关详情,建议您查看oracle tutorials

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:嗨,我是本!   来自客户#3:嗨,我是安妮!   来自客户#4:嗨,我是安东!

答案 1 :(得分:1)

这是直到正在进行中的工作,请耐心等待......最初只是针对可能的模型发表的评论。今晚晚些时候会有更新。

不同型号

一般来说,我们想到的是这三种方法:

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

这些不仅限于网络的客户端 - 服务器模型 虽然编程,但看到它们显示出来是相当普遍的 其他场景,如GUI编程。

多进程模型

//传入

多线程模型

//传入

事件循环模型

event loop是程序等待传入的构造 将消息发送到系统其他部分的说明。它的主要 优势在于其实施的简单性和在其中 轻量级方面。

如果您的主程序不执行任何操作,这将特别有用 计算密集和长时间的动作,以免阻塞 下一次传入连接时间过长。这个想法只是等待,得到 连接,发送到单独的子系统以执行某些操作, 等待回答,直到你能回复为止。

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


更新

根据您的最新评论,在我看来,您受到一些严格的时间限制,也许您应该推迟学习体验,享受开发速度。出于这个原因,我建议你考虑使用一个框架,让你可以覆盖你的基地,并为你照顾残骸 - 我原本以为你是把这作为个人努力的一部分,并试图学习。我会重新确定优先顺序并专注于完成任务,即使理解可能最初会受到影响。

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