C# 服务器和 Java 客户端:TCP 套接字通信问题

2024-04-04

我用 C# 编写了一个服务器程序TCPListner和一个使用套接字的 Java 客户端程序,但我无法将复杂的对象从 Java 客户端发送到 C# 服务器。

当我通过将字符串转换为字节数组从 Java 客户端发送到 C# 服务器时, 当转换回字符串时,它总是在消息开头显示一些无效字符(使用Encoding.utf8.getstring(bytesArray))在 C# 服务器中。当我将字符串从 C# 传递到 Java 客户端时,它显示无效标头错误。

如果有人有任何替代方案或知道任何可以解决我的问题的免费 API,请帮助我。我努力了Java-cs-bridge发送复杂对象,但它总是在 C# 服务器上显示异常。

这是代码:

C# 服务器代码 - 主要功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;

namespace netSocketServer
{
    class Program
    {
        static void Main(string[] args)
        {
            TcpListener server = new TcpListener(IPAddress.Any, 8888);

            var IP = Dns.GetHostEntry(Dns.GetHostName()).AddressList.Where(ip =>ip.AddressFamily == AddressFamily.InterNetwork).Select(ip =>ip).FirstOrDefault();

            server.Start();
            Console.WriteLine("Server is Running at " + IP.ToString());


            TcpClient clientSocket = server.AcceptTcpClient();
            Console.WriteLine("Client Connected ... ");

            Writer wr = new Writer(clientSocket);
           wr.start(); 

            Reader r = new Reader(clientSocket);
            r.start();

            Console.Read();
        }
    }
}

C# 服务器读取器类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.IO;


namespace netSocketServer
{
    class Reader
    {
        TcpClient socket;
        NetworkStream ns;

        public Reader(TcpClient s)
        {
            socket = s;
            ns = socket.GetStream() ;
        }
        public void start() 
        {
            new Thread(
                t => {
                    while (true)
                    {
                        try
                        {
                            int size = ns.ReadByte();
                            byte[] buff = new byte[size];

                            ns.Read(buff,0,size);

                            String message = Encoding.UTF8.getString(buff);

                            Console.WriteLine("Message from Client : {0}",message);

                            ns.Flush();
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("Client Disconnected : " + e.Message);
                        }
                    }
                }).Start();
        } 

    }
}

C# 服务器编写器类

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace netSocketServer
{
    class Writer
    {
        TcpClient socket;
        NetworkStream ns;

        public Writer(TcpClient s)
        {
            socket = s;
            ns = socket.GetStream();
        }
        public void start() 
        {
            new Thread(
                t => {
                    while (true)
                    {
                        try
                        {
                            Console.Write("Please Enter your Message : ");
                            string Message = Console.ReadLine();
                            byte[] buff = Encoding.UTF8.GetBytes(Message);
                            byte size = (byte)Message.Length;
                            ns.WriteByte(size);
                            ns.Write(buff, 0, buff.Length);
                            ns.Flush();
                        }
                        catch(IOException e)
                        {
                            Console.WriteLine("Client Disconnected : " + e.Message);
                            socket.Close();
                            Thread.CurrentThread.Abort();
                            Console.WriteLine("Press any key to Closse Server .... ");
                        }
                    }
                }).Start();
        } 

    }
}

Java客户端-主要功能

package javaclient.net;

import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
/**
 *
 * @author Numan
 */
public class JavaClientNet {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) 
    {
        Socket socket;
        Read r;
        Writer wr;

        Scanner s = new Scanner(System.in);

        try 
        {
            // TODO code application logic here

            System.out.print("Please Enter Server IP : ");
            socket = new Socket(s.next(), 8888);

            wr = new Writer(socket);
            wr.start();

            r = new Read(socket);
            r.start();
        }
        catch (IOException ex) 
        {
            System.out.println(ex.getMessage());   
        }
    }
}

Java 客户端 - Reader 类

package javaclient.net;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;

/**
 *
 * @author Numan
 */
public class Read extends Thread
{
    Socket socket;
    ObjectInputStream inStream;

    Read(Socket s)
    {
        socket = s;
        try {
            inStream = new ObjectInputStream(socket.getInputStream());
            }
        catch (IOException ex) 
        {
            System.out.println(ex.getMessage());
        }
    }
    @Override
    public void run()
    {
     while(true)
     {
         try
         {
            String str;
            byte size = inStream.readByte();
            byte[] buf = new byte[size];
            inStream.read(buf);
            str = new String(buf);
            System.out.println("Message form Server : "+str);
         }
         catch(IOException e)
         {
             System.out.println(e.getMessage());
             Thread.currentThread().stop();
         } 
     }   
    }
}

Java 客户端 - Writer 类

package javaclient.net;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;
import javacsconverter.core.tobyte.ToByteConvertHelper;

/**
 *
 * @author Numan
 */
public class Writer extends Thread
{
    Socket socket;
    ObjectOutputStream outStream;
    Scanner scanner = new Scanner(System.in);


    Writer(Socket s)
    {
        socket =s;
        try 
        {
            outStream = new ObjectOutputStream(socket.getOutputStream());
        }
        catch (IOException ex) 
        {
            System.out.println(ex.getMessage());
        }
    }
    @Override
    public void run()
    {
        while(true)
        {
            try 
            {

                System.out.print("Please Enter Your Message : ");


                String str = scanner.nextLine();

                byte[] buff = str.getBytes();

                outStream.write(buff);

                outStream.flush();
            } 
            catch (IOException ex) 
            {
                System.out.println(ex.getMessage());
            }

        }
    }
}

一般注意事项

请不要中止线程(C# 和 Java)。

C# 服务器

节目类

由于静态,存在数据竞争Console类被多个线程使用:

  1. 主线程:Program.Main()方法调用Console.Read() method;
  2. 工作线程: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();

希望这可以帮助!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 服务器和 Java 客户端:TCP 套接字通信问题 的相关文章

随机推荐