为什么我的服务器(使用 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) {
我正在创建一个 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) {