实现NIO多线程服务器后出现的接收数据包不全的问题

liounzhou 2004-10-17 12:43:37
小弟看了<<基于事件的NIO多线程服务器>>一文后,动手做了个类似的服务器端。
读取数据包的类如下:
package com.bestleader.comm.nio;

import java.util.List;
import java.util.LinkedList;
import java.nio.channels.SocketChannel;
import java.nio.channels.SelectionKey;
import java.nio.ByteBuffer;
import java.io.IOException;
import com.bestleader.comm.logs.Logs;

/**
* <p>Title: 读线程</p>
* <p>Description: 该线程用于读取客户端数据</p>
* @author starboy
* @version 1.0
*/

public class Reader extends Thread {
private static int BUFFER_SIZE = 1024;
private static List pool = new LinkedList();
private static Notifier notifier = Notifier.getNotifier();

public Reader() {
}

public void run() {
while (true) {
try {
SelectionKey key;
synchronized (pool) {
while (pool.isEmpty()) {
pool.wait();
}
key = (SelectionKey) pool.remove(0);
}

// 读取数据
read(key);
}
catch (Exception e) {
continue;
}
}
}

/**
* 读取客户端发出请求数据
* @param sc 套接通道
*/
public static byte[] readRequest(SocketChannel sc) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
int off = 0;
int r = 0;
byte[] data = new byte[BUFFER_SIZE * 10];

while ( true ) {
buffer.clear();
r = sc.read(buffer);
if (r == -1|| r == 0) break;
//问题出在这里:加了这部分代码才能收完数据包
if (r == 12){
try{
//Logs是自己写的一个输出到文本框的输出类
Logs.println("12");
}
catch(Exception ex){ }
}//以上是问题所在的代码
if ( (off + r) > data.length) {
data = grow(data, BUFFER_SIZE * 10);
}
byte[] buf = buffer.array();
System.arraycopy(buf, 0, data, off, r);
off += r;
}
byte[] req = new byte[off];
System.arraycopy(data, 0, req, 0, off);
return req;
}

/**
* 处理连接数据读取
* @param key SelectionKey
*/
public void read(SelectionKey key) {
try {
// 读取客户端数据
SocketChannel sc = (SocketChannel) key.channel();
byte[] clientData = readRequest(sc);

Request request = (Request)key.attachment();
request.setDataInput(clientData);

// 触发onRead
notifier.fireOnRead(request);

// 提交主控线程进行写处理
Server.processWriteRequest(key);
}
catch (Exception e) {
notifier.fireOnError("Error occured in Reader: " + e.getMessage());
}
}

/**
* 处理客户请求,管理用户的联结池,并唤醒队列中的线程进行处理
*/
public static void processRequest(SelectionKey key) {
synchronized (pool) {
pool.add(pool.size(), key);
pool.notifyAll();
}
}

/**
* 数组扩容
* @param src byte[] 源数组数据
* @param size int 扩容的增加量
* @return byte[] 扩容后的数组
*/
public static byte[] grow(byte[] src, int size) {
byte[] tmp = new byte[src.length + size];
System.arraycopy(src, 0, tmp, 0, src.length);
return tmp;
}
}

客户端有两种,一个是用C++写的网关程序,一个是用JAVA写的业务处理程序,它们之间传递的数据包都是XML格式,但会在数据包前面加上12位的byte来记录数据包的长度。现在的问题是,用C++写的网关程序,在传递数据包的时候,经常出现只收到包头(也就是只有12位长,记录数据包长度的byte),后面的数据包内容收不到;而如果单步调试这部分代码,就会分成两次接收(能够全部收完)。用JAVA写的程序传过来的数据则没有这个问题。给我的感觉是:C++传过来的包分成了两部分(正常情况下应该是一个整体),如果不加输出的代码,就不能收全数据。加wait()延时也没用,用System.out.print()输出也没用,目前只发现用自己写的输出类才能把分成的两部分数据全接收。各位大侠,这会是什么问题啊?
...全文
312 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
liounzhou 2004-10-18
  • 打赏
  • 举报
回复
to shangqiao(伤桥):为什么要对Channel做处理呢?我读数据时,长度为12位的话,就会做一个延时的操作,然后再次读数据。如果没有延时的操作,再次读数据就会为空。
nwpulipeng 2004-10-18
  • 打赏
  • 举报
回复
up顶
shangqiao 2004-10-18
  • 打赏
  • 举报
回复
//问题出在这里:加了这部分代码才能收完数据包
if (r == 12){
try{
//Logs是自己写的一个输出到文本框的输出类
Logs.println("12");
}
catch(Exception ex){ }
}//以上是问题所在的代码
你这段代码没有对Channel处理吧
shangqiao 2004-10-18
  • 打赏
  • 举报
回复
/**
* Channel copy method 2. This method performs the same copy, but
* assures the temp buffer is empty before reading more data. This
* never requires data copying but may result in more systems calls.
* No post-loop cleanup is needed because the buffer will be empty
* when the loop is exited.
*/
68
private static void channelCopy2 (ReadableByteChannel src,
WritableByteChannel dest)
throws IOException
{
ByteBuffer buffer = ByteBuffer.allocateDirect (16 * 1024);
while (src.read (buffer) != -1) {
// Prepare the buffer to be drained
buffer.flip();
// Make sure that the buffer was fully drained
while (buffer.hasRemaining()) {
dest.write (buffer);
}
// Make the buffer empty, ready for filling
buffer.clear();
}
}
shangqiao 2004-10-18
  • 打赏
  • 举报
回复
/**
* Channel copy method 1. This method copies data from the src
* channel and writes it to the dest channel until EOF on src.
* This implementation makes use of compact() on the temp buffer
* to pack down the data if the buffer wasn't fully drained. This
* may result in data copying, but minimizes system calls. It also
* requires a cleanup loop to make sure all the data gets sent.
*/
shangqiao 2004-10-18
  • 打赏
  • 举报
回复
private static void channelCopy1 (ReadableByteChannel src,
WritableByteChannel dest)
throws IOException
{
ByteBuffer buffer = ByteBuffer.allocateDirect (16 * 1024);
while (src.read (buffer) != -1) {
// Prepare the buffer to be drained
buffer.flip();
// Write to the channel; may block
dest.write (buffer);
// If partial transfer, shift remainder down
// If buffer is empty, same as doing clear()
buffer.compact();
}
// EOF will leave buffer in fill state
buffer.flip();
// Make sure that the buffer is fully drained
while (buffer.hasRemaining()) {
dest.write (buffer);
}
}
liounzhou 2004-10-18
  • 打赏
  • 举报
回复
没有人知道吗?
tom2005 2004-10-18
  • 打赏
  • 举报
回复
up

62,612

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧