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

时间:2015-05-29 07:34:06

标签: java c# sockets tcp client-server

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

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

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

以下是代码:

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

        }
    }
}

1 个答案:

答案 0 :(得分:4)

一般说明

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

C#Server

计划类

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

  1. 主线程:Program.Main()方法调用Console.Read()方法;
  2. 工作线程:Writer.start()方法调用Console.ReadLine()方法。
  3. 请考虑将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);
            }
        }
        ...
    }
    

    作家类

    首先,要描述和确定字节,文本流的顺序考虑包括每个消息的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();
    

    希望这有帮助!