1

我为服务器和客户端编写了一些代码,以将文件从服务器传输到客户端,它就像一个魅力;但是我有几个问题。我想在 GUI 下构建此代码,并且我想列出文件夹中的所有文件,但是如何让客户在看到提供的文件列表后选择他想要的文件(如何将字符串发送到服务器以选择文件)?

服务器代码

import java.io.*;
import java.net.*;



class TCPServer {

    public static void listfile(){

    File folder = new File("c:/");
    File[] listOfFiles = folder.listFiles();

    for (int i = 0; i < listOfFiles.length; i++) {
      if (listOfFiles[i].isFile()) {
        System.out.println("File " + listOfFiles[i].getName());
      } else if (listOfFiles[i].isDirectory()) {
        System.out.println("Directory " + listOfFiles[i].getName());
      }
    }
  }


    public static void main(String args[]) {

        listfile();

        while (true) {
            ServerSocket welcomeSocket = null;
            Socket connectionSocket = null;
            BufferedOutputStream outToClient = null;

            try {
                welcomeSocket = new ServerSocket(3248);
                connectionSocket = welcomeSocket.accept();
                outToClient = new BufferedOutputStream(connectionSocket.getOutputStream());
            } catch (IOException ex) {
                // Do exception handling
            }


            if (outToClient != null) {

                String FileName = "carexception.java";

                File myFile = new File("C:\\"+FileName);

                byte[] mybytearray = new byte[(int) myFile.length()];

                FileInputStream fis = null;

                try {
                    fis = new FileInputStream(myFile);
                } catch (FileNotFoundException ex) {
                    // Do exception handling
                }
                BufferedInputStream bis = new BufferedInputStream(fis);

                try {
                    bis.read(mybytearray, 0, mybytearray.length);
                    outToClient.write(mybytearray, 0, mybytearray.length);
                    outToClient.flush();
                    outToClient.close();
                    connectionSocket.close();

                    // File sent, exit the main method
                    return;
                } catch (IOException ex) {
                    // Do exception handling
                }
            }

        }
    }
}

客户代码

import java.io.*;
import java.net.*;
import java.util.*;

class TCPClient {

    public static void main(String args[]) {
        Scanner s = new Scanner(System.in);
        byte[] aByte = new byte[1];
        int bytesRead;

        Socket clientSocket = null;
        InputStream is = null;

        try {
            clientSocket = new Socket("127.0.0.1", 3248);
            is = clientSocket.getInputStream();
        } catch (IOException ex) {
            // Do exception handling
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        if (is != null) {

            FileOutputStream fos = null;
            BufferedOutputStream bos = null;
            try {
                fos = new FileOutputStream("E:\\sss.java");
                bos = new BufferedOutputStream(fos);
                bytesRead = is.read(aByte, 0, aByte.length);

                do {
                        baos.write(aByte);
                        bytesRead = is.read(aByte);
                } while (bytesRead != -1);

                bos.write(baos.toByteArray());
                bos.flush();
                bos.close();
                clientSocket.close();
            } catch (IOException ex) {
                // Do exception handling
            }
        }
    }
}
4

4 回答 4

3

为了完成你的目标,你必须改变很多事情。

您可以假设特定的协议顺序,即客户端需要向服务器发送请求以使服务器执行任何操作,因此在建立连接时服务器始终处于侦听状态。

你应该,

  1. 引入发送请求和接收响应的循环
  2. 弄清楚如何发送一个字符串对象
  3. 分解文件发送部分,这样您就不会分配比操作系统可以提供的更大的字节数组(例如,考虑一个 4GB 的文件,为整个文件分配一个字节数组可能很麻烦)

因此,考虑到这一点,我们可以开始了。关于步骤 1,这可以使用 while 循环来完成。如果我们假设服务器总是监听一个请求,那么服务器“请求循环”可能看起来像这样。

ClientRequest request;
while (request.getType() != RequestType.Complete) {
    // receive new request
    // depending on type, send response
}

我们在这里简单地添加了两个类,一个ClientRequest封装来自客户端的消息,一个RequestType定义客户端感兴趣的请求类型的枚举,例如文件列表或文件内容。

public enum RequestType {
   None, Complete, RequestFileList, RequestFileContent
}

public class ClientRequest {
   private RequestType type;
   public ClientRequest() {
       type = RequestType.None;
   }

   public RequestType getType() {
       return type;
   }
}

现在我们需要以某种方式将它附加到套接字,因此我们添加了一个接收请求的方法,并将该请求分配给当前请求实例。

ClientRequest request = new ClientRequest();
while (request.getType() != RequestType.Complete) {
    // receive new request
    receiveRequest(clientSocket.getInputStream(), request);
    if (request.getType() != RequestType.Complete) {
        // pick a response
    }
}

private void receiveRequest(DataInputStream socketStream, ClientRequest request) {
    // get a type of request
    byte type = socketStream.readByte();
    request.setType(RequestType.from(type));
    // get parameters for request, depending on type
    if (request.getType() == RequestType.RequestFileContent) {
        // receive file id (file name for instance, or some other id that you prefer)
        String argument = readString(socketStream);
        request.setArgument(argument);
    }
}

from在 RequestType 中添加了一个方法,用于将一个字节转换为一个请求、一个setTypein 中的方法ClientRequest和一个readString方法。我们还在 ClientRequest 中添加了一个新字段以及相应的 get 和 set 方法。

public enum RequestType {
    // types as before
    ;
    public static RequestType from(byte b) {
        switch (b) {
            case 1: return RequestType.Complete;
            case 2: return RequestType.RequestFileList;
            case 3: return RequestType.RequestFileContent;
            default: return RequestType.None;
         }
    }
}

public class ClientRequest {
    private String argument;
    public void setType(RequestType value) {
        type = value;
    }

    public String getArgument() {
        return argument;
    }

    public void setArgument(String value) {
        this.argument = value;
    }
}

private String readString(DataInputStream socketStream) {
    int length = socketStream.readInt();
    byte[] stringBytes = new byte[length];
    socketStream.read(stringBytes);
    return new String(stringBytes, "UTF-8");
}

现在我们进入下一步,响应请求。只需添加一个开关盒并处理请求的类型。

{
    // depending on type, send response
    handleRequest(clientSocket.getOutputStream(), request);
}

private void handleRequest(DataOutputStream socketStream, ClientRequest request) {
    switch (request.getType()) {
        case RequestType.RequestFileList: {
            String[] fileList = getFileList(getCurrentDirectory());
            // send response type
            socketStream.write(ResponseType.ResponseFileList.getByte());
            // send number of files
            socketStream.writeInt(fileList.length);
            // send each string
            for (String fileName : fileList) {
                sendString(socketStream, fileName);
            }
        }
        break;
        case RequestType.RequestFileContent: {
            // send response type ResponseType.ResponseFileContent
            // send length of file so other party can determine number of bytes to receive
            // send file contents in chunks of a fixed byte array length
            // send last part of file contents, if length of file is not evenly divided by array chunk size
        }
        break;
    }
}

sendString 方法只是 readString 方法的“倒序”。

private void sendString(DataOutputStream socketStream, String value) {
    int length = value.length();
    socketStream.writeInt(length);
    byte[] stringBytes = value.getBytes("UTF-8");
    socketStream.write(stringBytes);
}

ResponseType是类似于 中的值的枚举,RequestType因此客户端可以处理服务器发送的响应类型。

通过这些更改,您将能够请求文件列表并显示服务器发送的文件的响应。当用户选择要接收的文件时,客户端可以向服务器发送新请求,服务器可以向客户端发送适当的文件内容。

客户端应用程序将必须定义一个类似的ClientRequest类(可能使用 name ServerResponse),其中包含服务器指定的用于读取和写入套接字流的相应方法。这可以通过将套接字封装在一个类中来进一步抽象,使用监听器模式来接收 GUI 可以订阅的请求或响应,..尽管这超出了我的示例。

如果您觉得我需要澄清任何事情,请发表评论,我会尽力回答。

于 2013-01-07T13:57:41.450 回答
1

你怎么问文件?按名字!我认为服务器接受命令并响应响应。您可以将命令的格式用于服务器:CMD_NAME, arg1, arg2, ... 参数可以是二进制的,取决于命令。通过 CMD_NAME,您的服务器将区分您想要的内容(接受文件或提供文件)。

您有一个问题,即您只接受一种类型的请求。您需要一个命令机制来向服务器询问不同的请求。服务器需要解析这些请求,而不是立即给出硬编码的答案。这将使其灵活。

http://www.javamex.com/tutorials/networking/simple_server_s_side.shtml我相信还有很多这样的例子。Java 套接字是可靠的,不会有问题。刚开始学习基础知识,如何在客户端和服务器之间传递不同的消息。但是,您的问题根本与套接字无关。想象一下,您通过文件进行通信:从一个文件读取请求并将响应写入另一个文件。那你会写哪些信息?这称为协议。你需要设计一个简单的。

于 2013-01-06T18:57:15.973 回答
0

您是否尝试过创建一个数组,所以每个文件都有自己的索引......当客户选择他想要的文件时,您返回某个数组索引上的文件。

〜顺便说一句,您可以序列化您的数组并将其发送给客户端。

于 2013-01-06T18:24:34.913 回答
0

您可以使用ObjectOutputStream发送字符串或任何其他类型的Objectvia writeObject

于 2013-01-06T19:50:27.023 回答