简体   繁体   English

如何使用 ZD52387880E1EA22817A72D3795 中的 TCP 协议在 sockets 上发送序列化的 class?

[英]How to send a serialized class over sockets using TCP protocal in Java?

To clarify, this class holds important client information that has to arrive in its entirety without missing or corrupted information.澄清一下,这个 class 包含重要的客户信息,这些信息必须完整地到达,而不会丢失或损坏信息。 I need to send this class as a variable that holds some client information from a client to a server, and the server stores that variable in an array.我需要将此 class 作为一个变量发送,该变量包含从客户端到服务器的一些客户端信息,并且服务器将该变量存储在一个数组中。

I could send it using ObjectOutputStream but is it safe to use this to send client information?我可以使用 ObjectOutputStream 发送它,但使用它发送客户端信息是否安全? If the client information arrives makes or breaks my project.如果客户信息到达,我的项目成败。 (I can only use java in my project) (我的项目中只能使用java)

I tried searching across the internet for a relateable solution but none was sufficient enough.我尝试在互联网上搜索相关的解决方案,但没有一个是足够的。

My knowledge specifically in how to explain the Java language in English is limited since I didn't learn this language using English, so I hope you understand my question.我在如何用英语解释 Java 语言方面的知识是有限的,因为我没有用英语学习这种语言,所以我希望你能理解我的问题。

EDIT: Added the class.编辑:添加了 class。

public class ClientInformation implements Serializable 
{
    /**
     * 
     */
    private static final long serialVersionUID = -8904366211043587433L;
    private int arrplace;
    private int mode;
    private int ip;
    private String myusername;
    private String username;
    private int password;
    private Dimension screenResolution;
    public ClientInformation (int ip, String myusername, String username, int password,Dimension screenResolution, int mode, int arrplace) {
        this.ip = ip;
        this.myusername = myusername;
        this.username = username;
        this.password = password;
        this.screenResolution = screenResolution;
        this.mode = mode;
    }
    public int getarrplace()
    {
        return arrplace;
    }
    public int getmode()
    {
        return mode;
    }
    public int getip()
    {
        return ip;
    }
    public String getmyusername()
    {
        return myusername;
    }
    public String getusername()
    {
        return username;
    }
    public int getpass()
    {
        return password;
    }
    public Dimension getscreenRes()
    {
        return screenResolution;
    }
    public void setarrplace(int arrplace)
    {
        this.arrplace = arrplace;
    }
    public void setmode (int mode)
    {
        this.mode = mode;
    }
    public void setmyusername (String myusername)
    {
        this.myusername = myusername;
    }
    public void setusername (String username)
    {
        this.username = username;
    }
    public void setpass(int password)
    {
        this.password = password;
    }
    public void setscreenRes(Dimension screenResolution)
    {
        this.screenResolution = screenResolution;
    }
}

Even if it is strongly discouraged to use serialization/deserialization of sensitive data classes, you can still implement it, but it is at least recommended to strictly follow the Oracle Java security guidelines in relation to that topic: 8 Serialization and Deserialization即使强烈建议不要使用敏感数据类的序列化/反序列化,您仍然可以实现它,但至少建议严格遵循与该主题相关的Oracle Java 安全指南8 序列化和反序列化
However, I also recommend you to use SSL Socket , instead of simple Java Socket, which guarantees the security of the communication channel, thus of your sensitive data that you're going to serialize over the ObjectOutputstream , and will prevent any tampering attempts from malicious users.但是,我还建议您使用SSL Socket ,而不是简单的 Java Socket ,这可以保证通信通道的安全性,从而保证您将通过ObjectOutputstream序列化的敏感数据,并防止任何恶意篡改尝试用户。
You can find some useful examples of SSLSocket usage at Java Cert Code standard page, at this link .您可以在此链接的 Java 证书代码标准页面上找到一些有用的 SSLSocket 使用示例。 Look at the "Compliant Solution" and try the example there.查看“合规解决方案”并尝试那里的示例。

Here you go.这里是 go。 I made an entire implementation for you, which writes its data directly on the stream.我为你做了一个完整的实现,它直接将其数据写入 stream。 I would recommand using eigher an SSL Socket or an encrypted stream (CipherInput- and CipherOutputStream).我建议使用 SSL 套接字或加密的 stream(CipherInput- 和 CipherOutputStream)。 To write this class to a stream just call writeTo on it and pass in an Outputstream or to read in pass an InputStream to its constructor.要将此 class 写入 stream 只需在其上调用 writeTo 并传入 Outputstream 或将 InputStream 传入其构造函数。

Note: DON'T forget to close (and flush) the streams after calling the corresponding methods.注意:不要忘记在调用相应的方法后关闭(并刷新)流。 I didn't make them close inside the writeTo method and the constructor, because you might still need the streams to read or write more data.我没有让它们在 writeTo 方法和构造函数中关闭,因为您可能仍然需要流来读取或写入更多数据。

Here you go (I tested it. It is fully functional and will even write and read null values correctly):这里是 go (我对其进行了测试。它功能齐全,甚至可以正确写入和读取 null 值):

public static final class ClientInformation implements Serializable {
    private static final long serialVersionUID = -8904366211043587433L;
    
    private static final Charset CHARSET = StandardCharsets.UTF_8;

    private int arrplace;
    private int mode;
    private int ip;
    private String myusername;
    private String username;
    private final int password;
    private Dimension screenResolution;

    public ClientInformation(int ip,
                             String myusername,
                             String username,
                             int password,
                             Dimension screenResolution,
                             int mode,
                             int arrplace) {
        this.ip = ip;
        this.myusername = myusername;
        this.username = username;
        this.password = password;
        this.screenResolution = screenResolution;
        this.mode = mode;
        this.arrplace = arrplace;
    }

    public ClientInformation(InputStream in) throws IOException {
        int l;
        byte[] sb = null, ib = new byte[4];

        // Read arrplace
        readFully(in, ib, 0, 4);
        arrplace = getInt(ib, 0);

        // Read mode
        readFully(in, ib, 0, 4);
        mode = getInt(ib, 0);

        // Read ip
        readFully(in, ib, 0, 4);
        ip = getInt(ib, 0);

        // Read myusername
        readFully(in, ib, 0, 4);
        l = getInt(ib, 0);
        sb = resize(sb, l);
        if (l >= 0) {
            readFully(in, sb, 0, l);
            myusername = new String(sb, 0, l, CHARSET);
        } else {
            myusername = null;
        }

        // Read username
        readFully(in, ib, 0, 4);
        l = getInt(ib, 0);
        sb = resize(sb, l);
        if (l >= 0) {
            readFully(in, sb, 0, l);
            username = new String(sb, 0, l, CHARSET);
        } else {
            username = null;
        }

        // Read password
        readFully(in, ib, 0, 4);
        password = getInt(ib, 0);

        // Read screenWidth
        readFully(in, ib, 0, 4);
        int screenWidth = getInt(ib, 0);

        // Read screenHeight
        readFully(in, ib, 0, 4);
        int screenHeight = getInt(ib, 0);

        screenResolution = new Dimension(
                screenWidth,
                screenHeight
        );
    }

    public void writeTo(OutputStream os) throws IOException {
        String s;
        int l;
        byte[] sb, ib = new byte[4];

        // Write arrplace
        putInt(ib, 0, arrplace);
        os.write(ib, 0, 4);

        // Write mode
        putInt(ib, 0, mode);
        os.write(ib, 0, 4);

        // Write ip
        putInt(ib, 0, ip);
        os.write(ib, 0, 4);

        // Write myusername
        s = myusername;
        if (s != null) {
            sb = s.getBytes(CHARSET);
            putInt(ib, 0, l = sb.length);
            os.write(ib, 0, 4);
            os.write(sb, 0, l);
        } else {
            putInt(ib, 0, -1);
            os.write(ib, 0, 4);
        }

        // Write username
        s = username;
        if (s != null) {
            sb = s.getBytes(CHARSET);
            putInt(ib, 0, l = sb.length);
            os.write(ib, 0, 4);
            os.write(sb, 0, l);
        } else {
            putInt(ib, 0, -1);
            os.write(ib, 0, 4);
        }

        // Write password
        putInt(ib, 0, password);
        os.write(ib, 0, 4);

        Dimension screenRes = screenResolution;

        // Write screenRes.getWidth()
        putInt(ib, 0, (int) screenRes.getWidth()); // Get width actually returns an integer
        os.write(ib, 0, 4);

        // Write screenRes.getHeight()
        putInt(ib, 0, (int) screenRes.getHeight()); // Get height actually returns an integer
        os.write(ib, 0, 4);
    }

    static byte[] resize(byte[] b, int newLen) {
        if (newLen < 0) return b;
        if (b == null || b.length < newLen) {
            return new byte[newLen];
        } else return b;
    }

    static void putInt(byte[] b, int off, int val) {
        b[off + 3] = (byte) (val);
        b[off + 2] = (byte) (val >>> 8);
        b[off + 1] = (byte) (val >>> 16);
        b[off] = (byte) (val >>> 24);
    }

    static int getInt(byte[] b, int off) {
        return ((b[off + 3] & 0xFF)) +
                ((b[off + 2] & 0xFF) << 8) +
                ((b[off + 1] & 0xFF) << 16) +
                ((b[off]) << 24);
    }

    static void readFully(InputStream in, byte[] b, int off, int len) throws IOException {
        int n = 0;
        while (n < len) {
            int count = in.read(b, off + n, len - n);
            if (count < 0) {
                throw new EOFException();
            }
            n += count;
        }
    }

    // Don't forget to add all the getters and setter you had
}

Here is the sample code I used to test this class:这是我用来测试这个 class 的示例代码:

try {
    // Serialize
    ClientInformation info = new ClientInformation(
            30,
            "MyUsername",
            "My Real Username",
            3485,
            new Dimension(300, 200),
            19,
            20
    );

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    info.writeTo(bos);
    bos.flush();

    // Deserialize
    ByteArrayInputStream in = new ByteArrayInputStream(bos.toByteArray());
    ClientInformation receivedInfo = new ClientInformation(in);

    System.out.println(receivedInfo.ip);
    System.out.println(receivedInfo.myusername);
    System.out.println(receivedInfo.username);
    System.out.println(receivedInfo.password);
    System.out.println(receivedInfo.screenResolution);
    System.out.println(receivedInfo.mode);
    System.out.println(receivedInfo.arrplace);
} catch (Throwable tr) {
    tr.printStackTrace();
}

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

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