【问题标题】:C#-Server and Java-Client: TCP Socket Communication IssuesC#-Server 和 Java-Client:TCP 套接字通信问题
【发布时间】:2015-05-29 07:34:05
【问题描述】:

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

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

如果有人有任何替代方案或知道任何可以解决我问题的免费 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 客户端 - 阅读器类

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 客户端 - 编写器类

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());
            }

        }
    }
}

【问题讨论】:

    标签: java c# sockets tcp client-server


    【解决方案1】:

    一般说明

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

    C# 服务器

    程序类

    因为静态Console 类被多个线程使用,所以存在数据竞争:

    1. 主线程:Program.Main()方法调用Console.Read()方法;
    2. 工作线程:Writer.start() 方法调用Console.ReadLine() 方法。

    请考虑将Program.Main() 方法的Console.Read() 方法调用替换为其他名称,例如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);
            }
        }
        ...
    }
    

    作家类

    首先,为了描述和确定文本流的字节顺序,考虑为每条消息包括the byte order mark (BOM)(例如)。

    另外,还有一个错误——发送了错误的“缓冲区长度”值。让我们看看原始实现:

    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 class, Java™ Platform Standard Ed. 7.

    流的实例化几乎是一样的:

    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();
    

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-20
      • 1970-01-01
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-18
      相关资源
      最近更新 更多