如何在 Java 中使用 TCP 协议通过套接字发送序列化的 class?
How to send a serialized class over sockets using TCP protocal in Java?
需要澄清的是,此 class 包含重要的客户信息,这些信息必须完整送达,且不得丢失或损坏信息。
我需要将此 class 作为一个变量发送,该变量包含从客户端到服务器的一些客户端信息,服务器将该变量存储在一个数组中。
我可以使用 ObjectOutputStream 发送它,但是使用它发送客户端信息安全吗?如果客户信息到达,我的项目就会成败。 (我的项目只能用java)
我尝试在互联网上搜索相关的解决方案,但 none 已经足够了。
我在如何用英语解释 Java 语言方面的知识有限,因为我不是用英语学习这种语言的,所以我希望你能理解我的问题。
编辑:添加了 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;
}
}
即使强烈反对使用serialization/deserialization敏感数据类,你仍然可以实现它,但至少建议严格遵循Oracle Java 与该主题相关的安全指南:
8 Serialization and Deserialization
但是,我还建议您使用 SSL Socket,而不是简单的 Java Socket,它可以保证通信通道的安全,从而保证您要访问的敏感数据的安全在 ObjectOutputstream
上进行序列化,并将防止恶意用户进行任何篡改尝试。
您可以在 Java 证书代码标准页面找到一些有用的 SSLSocket 用法示例,位于 link。查看“合规解决方案”并尝试那里的示例。
给你。我为您做了一个完整的实现,它将数据直接写入流中。我会推荐使用 eigher SSL 套接字或加密流(CipherInput 和 CipherOutputStream)。
要将此 class 写入流,只需对其调用 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.
给你(我测试过它。它功能齐全,甚至可以正确写入和读取空值):
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
}
这是我用来测试这个的示例代码 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();
}
需要澄清的是,此 class 包含重要的客户信息,这些信息必须完整送达,且不得丢失或损坏信息。 我需要将此 class 作为一个变量发送,该变量包含从客户端到服务器的一些客户端信息,服务器将该变量存储在一个数组中。
我可以使用 ObjectOutputStream 发送它,但是使用它发送客户端信息安全吗?如果客户信息到达,我的项目就会成败。 (我的项目只能用java)
我尝试在互联网上搜索相关的解决方案,但 none 已经足够了。
我在如何用英语解释 Java 语言方面的知识有限,因为我不是用英语学习这种语言的,所以我希望你能理解我的问题。
编辑:添加了 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;
}
}
即使强烈反对使用serialization/deserialization敏感数据类,你仍然可以实现它,但至少建议严格遵循Oracle Java 与该主题相关的安全指南:
8 Serialization and Deserialization
但是,我还建议您使用 SSL Socket,而不是简单的 Java Socket,它可以保证通信通道的安全,从而保证您要访问的敏感数据的安全在 ObjectOutputstream
上进行序列化,并将防止恶意用户进行任何篡改尝试。
您可以在 Java 证书代码标准页面找到一些有用的 SSLSocket 用法示例,位于 link。查看“合规解决方案”并尝试那里的示例。
给你。我为您做了一个完整的实现,它将数据直接写入流中。我会推荐使用 eigher SSL 套接字或加密流(CipherInput 和 CipherOutputStream)。 要将此 class 写入流,只需对其调用 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.
给你(我测试过它。它功能齐全,甚至可以正确写入和读取空值):
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
}
这是我用来测试这个的示例代码 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();
}