为什么我的服务器(使用 BufferedReader)没有收到客户端发送的所有数据?

Why does my server (which uses BufferedReader) not receive all the data sent by the client?

我正在创建一个 Swing 白板应用程序,其中有一个输入设备和一个输出设备。使用输入设备的人可以在屏幕上作画,然后该屏幕会显示在输出设备(以及输入设备)上。

drawing/painting在输入设备(客户端)上运行良好,但我无法将坐标正确发送到服务器(输出设备)。

第一个问题是服务器中的BufferedReader没有立即接收到数据。即使我在输入设备上绘图,服务器也会在 30 秒后绘制线条。

第二个问题是服务器没有收到所有的坐标,即使它们是由客户端发送的,因此这些行是 incomplete/broken。

编辑: 我实现了一个计数器,我注意到服务器只接收客户端绘制的一半线的坐标对,从而导致图像不完整。此外,它以另一种方式接收它们。 (即,如果客户端发送 line1、line2、line3、line4 的坐标,服务器仅接收 line1 和 line3(这就是显示不完整图像的原因)。

这是我在 mouseDragged ActionListener 中的代码。

g2.drawLine(oldX, oldY, currentX, currentY);
sendData();
public void sendData() { 
        // I am using a PrintWriter as the 'out'
        out.println(oldX + " " + oldY + " " + currentX + " " + currentY);
    }

这是服务器接收数据的代码

while (true) { 
    if (in.readLine() != null) { 
        String message = in.readLine();
        System.out.println(i + ": " + message);
        drawOnImage(message);
        i++;
    }
}

文件中的完整代码如下。

客户端


import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;

import javax.swing.JComponent;

public class DrawArea extends JComponent {

    // Image in which we're going to draw
    private Image image;
    // Graphics2D object ==> used to draw on
    private Graphics2D g2;
    // Mouse coordinates
    private int currentX, currentY, oldX, oldY;



    Socket socket;
    BufferedReader in;
    PrintWriter out;

    public DrawArea() throws IOException {
        setDoubleBuffered(false);


        socket = new Socket("localhost", 7777); // connecting to correct port.
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out = new PrintWriter(socket.getOutputStream());

        System.out.println("Connected");

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                // save coord x,y when mouse is pressed
                oldX = e.getX();
                oldY = e.getY();
            }
        });


        ArrayList<String> coords = new ArrayList();
        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                // coord x,y when drag mouse
                currentX = e.getX();
                currentY = e.getY();



                if (g2 != null) {
                    // draw line if g2 context not null
                    g2.drawLine(oldX, oldY, currentX, currentY);
                    sendData();

                    coords.add(oldX + "," + oldY + " " + currentX + "," + currentY);
                    System.out.println(coords.size());
                    System.out.println(oldX + "," + oldY + " " + currentX + "," + currentY);

                    // refresh draw area to repaint
                    repaint();
                    // store current coords x,y as olds x,y
                    oldX = currentX;
                    oldY = currentY;
                }
            }
        });
    }



    public void sendData() { 
        out.println(oldX + " " + oldY + " " + currentX + " " + currentY);
    }
    @Override
    protected void paintComponent(Graphics g) {
        if (image == null) {
            // image to draw null ==> we create
            image = createImage(getSize().width, getSize().height);
            g2 = (Graphics2D) image.getGraphics();
            // enable antialiasing
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            // clear draw area
            clear();
        }

        g.drawImage(image, 0, 0, null);
    }

       public void clear() {
        g2.setPaint(Color.white);
        // draw white on entire draw area to clear
        g2.fillRect(0, 0, getSize().width, getSize().height);
        g2.setPaint(Color.black);
        repaint();
    }

服务器端

import java.net.*;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Server implements Runnable {

    static ServerSocket serverSocket;

    static Socket socket;


    BufferedReader in; // reading. receiving data.



    TestDraw drawOutput; // this is the canvas on which the lines are drawn

    public Server(TestDraw drawOutput) {
        this.drawOutput = drawOutput;
    }


    @Override
    public void run() {
        try {
            System.out.println("Running");

            serverSocket = new ServerSocket(7777);

            socket = serverSocket.accept();

            System.out.println("A Client Connected");


            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));



            draw = false;


            int i = 1;
            while (true) { 
                if (in.readLine() != null) { 
                    String message = in.readLine();
                    System.out.println(i + ": " + message);
                    drawOnImage(message);
                    i++;
                }
            }



        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);

        } 

    }


    void drawOnImage(String message) {

    String[] splitted = message.split(" ");
    int pointOneX = Integer.parseInt(splitted[0]);
    int pointOneY = Integer.parseInt(splitted[1]);
    int pointTwoX = Integer.parseInt(splitted[2]);
    int pointTwoY = Integer.parseInt(splitted[3]);


    drawOutput.g2.drawLine(pointOneX,pointOneY,pointTwoX,pointTwoY);

    drawOutput.repaint();

    }
}

PrintWriter 和网络套接字在内部缓冲区中累积数据。缓冲区需要"flushed"以确保尽快发送数据。

一种方法是在构建 PrintWriter 时设置 "autoflush" 属性。这将确保缓冲区在每次调用 println.

时被刷新
out = new PrintWriter(socket.getOutputStream(), true);

另一种方法是在您认为需要时自己调用 flush,例如:

out.println(oldX + " " + oldY + " " + currentX + " " + currentY);
out.flush();

至于接收方的问题,是因为调用了两次readLine。每次调用都会读取一个新行,而您基本上是在丢弃第一行。

改变

if (in.readLine() != null) { 
    String message = in.readLine();

String message = in.readLine();
if (message != null) {