套接字:使用readUTF()和writeUTF()通过套接字的错误请求 - 响应

时间:2014-05-12 09:09:26

标签: java sockets

这是一个简单的控制台应用程序,由服务器端和客户端组成。服务器端侦听某个端口,当客户端连接服务器时开始以类似菜单的方式发送问题。例如:

  
      
  1. 打印存在的数据
  2.   
  3. 添加新数据
  4.   
  5. 编辑现有数据
  6.   
  7. 删除现有数据
  8.   

客户端轮流发送菜单号并等待服务器的反应。每个命令服务器必须再次发送菜单列表。

正如您将从下面的示例中看到的那样,客户端连接到服务器并读取其菜单,然后开始与之交互。但这种互动并不像预期的那样顺利。有时数据是按件发送的。每个客户端响应后应按Enter键两次,以便从服务器获得响应。

这里准备编译我在我的应用程序中运行的问题的示例:

服务器(一个Java项目):

public class TestServer {
    private static List<String> example;
    static {
        example = new ArrayList<String>();
        for(int i = 0; i < 5; ++i) {
            example.add("Data#" + i);
        }
    }

    private static DataInputStream in;
    private static DataOutputStream out;

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(30000);
        System.out.println("*** Waiting for a client...");

        Socket socket = serverSocket.accept();
        System.out.println("*** Client connected!\n");

        in = new DataInputStream(socket.getInputStream());
        out = new DataOutputStream(socket.getOutputStream());

        while (true) {
            showUserMenu();

            String clientMessage = in.readUTF();
            System.out.println("Client made a choice: " + clientMessage);

            try {
                int answerChoice = Integer.parseInt(clientMessage);
                analyzeSelection(answerChoice);
                System.out.println("Waiting for client menu choice...\n");
            } catch (NumberFormatException e) {
                System.out.println(e.getMessage());
            }

        }
    }

    private static void analyzeSelection(int answerChoice) {
        try {
            switch(answerChoice) {
                case 1:
                    printExistData();
                    break;
                case 2:
                    //2. Add data
                    createNewData();
                    out.writeUTF("New data added!");
                    out.flush();
                    break;
                default:
                    out.writeUTF("There is no such menu item. Please enter correct menu number");
                    out.flush();
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void createNewData() throws IOException {
        StringBuilder builder = new StringBuilder();
        out.writeUTF("Do you want to set change name? (y/n)");
        out.flush();
        if(isYes()) {
            out.writeUTF("Enter data name: ");
            out.flush();
            builder.append(in.readUTF());
        }
        out.writeUTF("Do you want to set data value? (y/n)");
        if(isYes()) {
            out.writeUTF("Enter data value: ");
            out.flush();
            builder.append(in.readUTF());
        }
        example.add(builder.toString());
    }

    private static boolean isYes() throws IOException {
        return in.readUTF().toLowerCase().equals("y");
    }

    private static void printExistData() throws IOException {
        StringBuilder builder = new StringBuilder();
        for(String s : example) {
            builder.append("- ").append(s).append("\n");
        }
        out.writeUTF(builder.toString());
        out.flush();
    }

    private static void showUserMenu() throws IOException {
        StringBuilder builder = new StringBuilder();
        builder.append("1. Print exist data\n").append("2. Add new data\n")
               .append("*** Type menu number and press Enter.");
        out.writeUTF(builder.toString());
        out.flush();
    }
}

客户端(另一个Java项目):

public class TestClient {
    public static void main(String[] args) throws IOException {
        InetAddress ipAddress = InetAddress.getByName("127.0.0.1");
        System.out.println("*** Ready to connect to 127.0.0.1:30000");

        Socket socket = new Socket(ipAddress, 30000);
        System.out.println("*** Connection established!");

        DataInputStream in = new DataInputStream(socket.getInputStream());
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());

        //Read client input from keyboard
        BufferedReader keyboard = new BufferedReader(new InputStreamReader(System.in));

        while(true) {
            String serverResponse = in.readUTF();
            System.out.println(serverResponse);

            String clientResponse = keyboard.readLine();
            out.writeUTF(clientResponse);
            out.flush();
        }
    }
}

这是客户端和服务器之间通信的总体输出:

[SERVER]*** Waiting for a client...
[CLIENT]*** Ready to connect to 127.0.0.1:30000
[CLIENT]*** Connection established!
[SERVER]*** Client connected!
[CLIENT]1. Print exist data
2. Add new data
*** Type menu number and press Enter.
[CLIENT]1
[SERVER]Client made a choice: 1
[CLIENT]- Data#0
- Data#1
- Data#2
- Data#3
- Data#4
[SERVER]Waiting for client menu choice...
[CLIENT] //Press Enter because no menu was printed
[SERVER]Client made a choice: 
[SERVER]For input string: "" //NumberFormatException (NFE)
[CLIENT]1. Print exist data
2. Add new data
*** Type menu number and press Enter.
[CLIENT]2
[SERVER]Client made a choice: 2
[CLIENT]1. Print exist data
2. Add new data
*** Type menu number and press Enter.
[CLIENT]2
[CLIENT]Do you want to set change name? (y/n)
[CLIENT]y
[CLIENT]Do you want to set data value? (y/n)
[CLIENT]y
[CLIENT]Enter data value:
[SERVER]Waiting for client menu choice...
[CLIENT]123
[SERVER]Client made a choice: 123
[SERVER]Waiting for client menu choice...
[CLIENT]New data added!
[CLIENT] //Press Enter because no menu was printed
[SERVER]Client made a choice: 
[SERVER]For input string: "" //NFE
...

我发现存在某种同步问题。但我无法理解为什么会这样。

此通讯架构有什么问题?使客户端 - 服务器通信更通用的常用方法是什么?

1 个答案:

答案 0 :(得分:0)

您的流不应该是静态的,它们甚至不应该是服务器类的成员。它们应该是Runnable类的非静态成员,您应该在创建应创建的新线程时对其进行实例化以处理每个连接。