简体   繁体   English

C#-Server和Java-Client:TCP套接字通信问题

[英]C#-Server and Java-Client: TCP Socket Communication Issues

I have wrote a server program in C# using TCPListner and a client program in Java using socket but I fail to send complex objects from Java client to C# server. 我使用TCPListner在C#中编写了一个服务器程序,使用socket在Java中TCPListner了一个客户端程序,但是我无法将复杂的对象从Java客户端发送到C#服务器。

When I send a simple string from Java client to C# server by converting the string into byte array, it always show some invalid characters at the start of message when converted back to String (using Encoding.utf8.getstring(bytesArray) ) in C# server. 当我通过将字符串转换为字节数组从Java客户端向C#服务器发送一个简单的字符串时,它总是在C#server中转换回String(使用Encoding.utf8.getstring(bytesArray) )时在消息的开头显示一些无效字符。 When I pass a String from C# to Java Client it shows invalid Header error. 当我从C#传递一个字符串到Java客户端时,它显示无效的标头错误。

Please help me if any one have any alternative or know abut any free API which can solve my problem. 如果任何人有任何替代或知道任何可以解决我的问题的免费API,请帮助我。 I have tried Java-cs-bridge to send complex objects but it always show Exception on C# server. 我已经尝试过Java-cs-bridge来发送复杂的对象,但它总是在C#服务器上显示Exception。

Here is the code: 这是代码:

C# Server Code - Main Function 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# Server Reader Class C#Server Reader类

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# Server Writer Class 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 Client - Main Function 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 Client - Reader Class 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 Client - Writer Class 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());
            }

        }
    }
}

General notes 一般注意事项

Please do not abort the threads (both C# and Java). 请不要中止线程(C#和Java)。

C# Server C#服务器

Program class 课程班

There is a data race because the static Console class is used by multiple threads: 存在数据争用,因为多个线程使用静态Console类:

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

Please consider replacing the Console.Read() method call of the Program.Main() method with something different, for example, Thread.Sleep(Timeout.Infinite) . 请考虑使用不同的东西替换Program.Main()方法的Console.Read()方法调用,例如Thread.Sleep(Timeout.Infinite)

Reader class 读者课

There is a mistake — the Stream.Read() method is not guaranteed to read the array of the specified "size" at once (one call), the return value should be used to determine the actual number of bytes read. 有一个错误 - Stream.Read()方法不能保证一次读取指定“大小”的数组(一次调用),返回值应该用于确定读取的实际字节数。 Let's see the original implementation: 让我们看看最初的实现:

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

Corrected version: 更正版本:

/// <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);
        }
    }
    ...
}

Writer class 作家班

First of all, to describe and determine byte the order of the text stream consider including the byte order mark (BOM) for each message (for example). 首先,要描述和确定字节,文本流的顺序考虑包括每个消息的字节顺序标记(BOM) (例如)。

Also, there is a mistake — wrong "buffer length" value is sent. 此外,还有一个错误 - 发送错误的“缓冲区长度”值。 Let's see the original implementation: 让我们看看最初的实现:

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

Corrected version: 更正版本:

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

Alternative corrected version — the StreamWriter class is used to encode the string as UTF-8 with the byte order mark (BOM): 备用更正版本 - 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 Client Java客户端

Read class 读课

First, please consider using DataInputStream class because the following statement is not true according to the question: 首先,请考虑使用DataInputStream类,因为根据以下问题,以下语句不正确:

An ObjectInputStream deserializes primitive data and objects previously written using an ObjectOutputStream. ObjectInputStream对先前使用ObjectOutputStream编写的基元数据和对象进行反序列化。

-- java.io.ObjectInputStream class, Java™ Platform Standard Ed. - java.io.ObjectInputStream类,Java™Platform Standard Ed。 7 . 7

The instantiation of the stream is almost the same: 流的实例化几乎相同:

inStream = new DataInputStream(socket.getInputStream());

Second, there is a mistake — reading the byte array, but ignoring the return value (actual number of bytes read): 第二,有一个错误 - 读取字节数组,但忽略返回值(读取的实际字节数):

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

Third, as stated above, the byte order mark (BOM) is included. 第三,如上所述,包括字节顺序标记(BOM)。

Corrected version: 更正版本:

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

Writer class 作家班

There is a mistake — the encoding is not specified explicitly. 有一个错误 - 没有明确指定编码。 Let's see the original implementation: 让我们看看最初的实现:

String str = scanner.nextLine();
byte[] buff = str.getBytes();

Corrected version: 更正版本:

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

Alternative corrected version — the ByteArrayOutputStream class is used to concatenate the arrays: 备用更正版本 - 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();

Hope this helps! 希望这可以帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM