一般注意事项
请不要中止线程(C# 和 Java)。
C# 服务器
节目类
由于静态,存在数据竞争Console
类被多个线程使用:
- 主线程:
Program.Main()
方法调用Console.Read()
method;
- 工作线程:
Writer.start()
方法调用Console.ReadLine()
method.
请考虑更换Console.Read()
的方法调用Program.Main()
方法与不同的东西,例如,Thread.Sleep(Timeout.Infinite)
.
读者班
有一个错误——Stream.Read()
方法不保证一次(一次调用)读取指定“大小”的数组,应使用返回值来确定实际读取的字节数。我们来看看原来的实现:
int size = ns.ReadByte();
byte[] buff = new byte[size];
// The Stream.Read() method does not guarantee to read the **whole array** "at once".
// Please use the return value of the method.
ns.Read(buff, 0, size);
String message = Encoding.UTF8.GetString(buff);
更正版本:
/// <summary>
/// Helper method to read the specified byte array (number of bytes to read is the size of the array).
/// </summary>
/// <param name="inputStream">Input stream.</param>
/// <param name="buffer">The output buffer.</param>
private static void ReadFully(Stream inputStream, byte[] buffer)
{
if (inputStream == null)
{
throw new ArgumentNullException("inputStream");
}
if (buffer == null)
{
throw new ArgumentNullException("buffer");
}
int totalBytesRead = 0;
int bytesLeft = buffer.Length;
if (bytesLeft <= 0)
{
throw new ArgumentException("There is nothing to read for the specified buffer", "buffer");
}
while (totalBytesRead < buffer.Length)
{
var bytesRead = inputStream.Read(buffer, totalBytesRead, bytesLeft);
if (bytesRead > 0)
{
totalBytesRead += bytesRead;
bytesLeft -= bytesRead;
}
else
{
throw new InvalidOperationException("Input stream reaches the end before reading all the bytes");
}
}
}
public void start()
{
...
int size = ns.ReadByte();
byte[] buff = new byte[size];
ReadFully(ns, buff);
using (var memoryStream = new MemoryStream(buff, false))
{
// The StreamReader class is used to extract the UTF-8 string which is encoded with the byte order mark (BOM).
using (var streamReader = new StreamReader(memoryStream, Encoding.UTF8))
{
string message = streamReader.ReadToEnd();
Console.WriteLine("Message from Client: {0}", message);
}
}
...
}
作家班
首先,要描述和确定文本流的字节顺序,请考虑包括字节顺序标记 (BOM) https://en.wikipedia.org/wiki/Byte_order_mark对于每条消息(例如)。
另外,还有一个错误——发送了错误的“缓冲区长度”值。我们来看看原来的实现:
string Message = Console.ReadLine();
byte[] buff = Encoding.UTF8.GetBytes(Message);
// Problem: instead of the length of the string, the size of byte array must be used
// because the UTF-8 encoding is used: generally, string length != "encoded number of bytes".
byte size = (byte)Message.Length;
ns.WriteByte(size);
ns.Write(buff, 0, buff.Length);
ns.Flush();
更正版本:
// UTF-8 with BOM.
var encoding = new UTF8Encoding(true);
// Buffer encoded as UTF-8 with BOM.
byte[] buff = encoding.GetPreamble()
.Concat(encoding.GetBytes(message))
.ToArray();
// Size of the encoded buffer.
byte size = Convert.ToByte(buff.Length);
ns.WriteByte(size);
ns.Write(buff, 0, buff.Length);
ns.Flush();
替代修正版本 —StreamWriter
类用于将字符串编码为带有字节顺序标记 (BOM) 的 UTF-8:
string message = Console.ReadLine();
using (var memoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8, 1024, true))
{
streamWriter.Write(message);
}
memoryStream.Flush();
byte size = Convert.ToByte(memoryStream.Length);
ns.WriteByte(size);
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(ns);
ns.Flush();
}
Java客户端
读课
首先,请考虑使用DataInputStream
类,因为根据问题,以下陈述不正确:
ObjectInputStream 反序列化先前使用 ObjectOutputStream 写入的原始数据和对象。
-- java.io.ObjectInputStream 类,Java™ 平台
标准版。 7 http://docs.oracle.com/javase/7/docs/api/java/io/ObjectInputStream.html.
流的实例化几乎是一样的:
inStream = new DataInputStream(socket.getInputStream());
其次,有一个错误——读取字节数组,但忽略返回值(实际读取的字节数):
String str;
byte size = inStream.readByte();
byte[] buf = new byte[size];
// The InputStream.read() method does not guarantee to read the **whole array** "at once".
// Please use the return value of the method.
inStream.read(buf);
str = new String(buf);
第三,如上所述,包括字节顺序标记(BOM)。
更正版本:
// Note: inStream must be an instance of DataInputStream class.
byte size = inStream.readByte();
byte[] buf = new byte[size];
// The DataInputStream.readFully() method reads the number of bytes required to fill the buffer entirely.
inStream.readFully(buf);
// Create in-memory stream for the byte array and read the UTF-8 string.
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
// The BOMInputStream class belongs to Apache Commons IO library.
BOMInputStream bomInputStream = new BOMInputStream(inputStream, false)) {
String charsetName = bomInputStream.getBOMCharsetName();
// The IOUtils class belongs to Apache Commons IO library.
String message = IOUtils.toString(bomInputStream, charsetName);
System.out.println("Message form Server : " + message);
}
作家班
有一个错误——编码没有明确指定。我们来看看原来的实现:
String str = scanner.nextLine();
byte[] buff = str.getBytes();
更正版本:
String str = scanner.nextLine();
byte[] byteOrderMarkBytes = ByteOrderMark.UTF_8.getBytes();
byte[] stringBytes = str.getBytes(StandardCharsets.UTF_8);
// The ArrayUtils.addAll() method belongs to Apache Commons Lang library.
byte[] buff = ArrayUtils.addAll(byteOrderMarkBytes, stringBytes);
outStream.writeByte(buff.length);
outStream.write(buff);
outStream.flush();
替代修正版本 —ByteArrayOutputStream
类用于连接数组:
String str = scanner.nextLine();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] byteOrderMarkBytes = ByteOrderMark.UTF_8.getBytes();
byteArrayOutputStream.write(byteOrderMarkBytes);
byte[] stringBytes = str.getBytes(StandardCharsets.UTF_8);
byteArrayOutputStream.write(stringBytes);
byteArrayOutputStream.flush();
byte[] buff = byteArrayOutputStream.toByteArray();
outStream.writeByte(buff.length);
outStream.write(buff);
outStream.flush();
希望这可以帮助!