最近在写一个类似于QQ的网络通讯项目,在信息发送的时候出现了一个问题,客户端的信息服务端可以正常收到并且转出,但是对应的客户端在接收的时候就会抛出这个异常,往往还会伴随着java.io.StreamCorruptedException: invalid type code: AC这个异常,我苦思冥想,翻来覆去的测试改代码,经过了一天的时间才找到问题所在,要明白这个异常为什么会出现我们首先需要了解对象输入输出流的一个特性
对象输入输出流的一个特点
首先介绍一下对象流的特性,相信出现这个问题的朋友都使用了对象输入输出流 ,经过这一天查询资料我才知道,当新生成一个对象输出流时它会向对应的接收方(可以是文件、网络端口等等)发送一个头信息,头信息包含了序列化流的魔数(magic number)和版本号等信息。
下面是对象输出流的构造方法:
java
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
其中的writeStreamHeader方法的功能就是发送头信息,代码如下
java
protected void writeStreamHeader() throws IOException {
bout.writeShort(STREAM_MAGIC);
bout.writeShort(STREAM_VERSION);
}
在对象输出流创建的时候就向接收方发送了头信息,同样的对象输入流在创建的时候也会调用一个读头信息的函数用来读取对应的头信息,表示接下来要接收来自于对方的各种信息,相当于一个输入流绑定上一个输出流。
问题代码
服务端(这里是精简后的代码)
java
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());
//问题就出现在上面这一行代码
Message message=(Message)ois.readObject();
if(message.getMesType().equals(MessageType.MESSAGE_COMMM_MES)){
String getter=message.getGetter();
System.out.println("用户:"+message.getSender()+"请求与"+getter+"通信");
ObjectOutputStream oos1=new ObjectOutputStream(ManageServerConnectClientThread.getServerConnectClientThread(getter).getSocket().getOutputStream());
oos1.writeObject(message);
System.out.println("客户端转发成功!");
}
客户端
java
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Message message=(Message)ois.readObject();//该线程会阻塞在这里直到服务端发送数据
if(message.getMesType().equals(MessageType.MESSAGE_COMMM_MES)){//普通消息
System.out.println("接收到来自用户"+message.getSender()+"的信息 :"+message.getContent());
System.out.println(message.getSendTime());//发送时间
问题所在:
我的程序会抛出这个异常是由于:在服务端的这边我为了图方便预先生成了一个对象输出流,这个对象输出流是和绑定的客户端相连接的,可是当遇到转发功能,即连接的客户端想要发送消息给别人的时候,服务端需要在生成一个新的对象输出流,然后在将消息转发出去
根据上面分析的对象输入输出流的特性,下面这个代码一次循环中服务端发送的消息有:
头信息(发送向客户端)+头信息(发送向客户端指定的客户端)+对象Message(发送向客户端指定的客户端)
如果客户端指定的对象是自己的话 ,那么就相当于服务端向客户端发送了两次头信息,外加发送的一个数据,而客户端那边只有一个对象输入流等待服务端的消息,新生成的对象输入流创建时只能接收一个头信息,然后在他们的数据通道中就会剩下:头信息(发送向客户端指定的客户端)+对象Message(发送向客户端指定的客户端),接下来调用readObject来读取对象肯定会抛出异常,因为读到了头信息,和想要的数据类型不匹配,所以如果我刚开始不为了图方便生成那个输出流就可以恢复正常。
java
ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());
在服务端中将这行代码删去即可
可能你们的代码和我的不太一样,但是既然是这个异常就说明一定是输入输出流不匹配的问题,按照我上面分析我的代码的思路方法去分析你的代码,相信你们可以解决问题的
总结
总结一下,对象输出流在创建时会自动发送头信息给对方,对面的接收方也会接收这个头信息,这大概是为了确认传输,类似于TCP的三次握手做一个连接确认吧,或是为了标记必要的连接信息,总之对于对象流的使用我们要在使用的地方再去定义生成,不要预先定义,不要预先定义,不要预先定义!!!!!!!!!
出于学习c语言时的坏习惯,我总是喜欢将后面要用到的变量都提前定义好,这才导致了这次的问题,找了一天的问题,各种断点调试,最后没想到是这个原因导致的,所幸最后顺序解决。
看到这里的朋友若还有细节问题可以私聊我