(求助)Java Socket通信Read函数阻塞问题

haizhixiang 2010-04-26 08:35:10
小弟最近在做Socket通信程序,遇到Java中Read阻塞问题,一时想不到什么好的解决办法,恳请各位大侠指点一二,谢谢!

具体问题如下:1、有什么办法能在读取Buffer时先知道Buffer里有多少字节的数据;
2、有什么办法不但能读到Buffer里的数据,而且还不会阻塞,不像read函数那样,当没有数据时就一直等待,直到有数据读出了才跳过;

据说采用NIO可以解决以上问题,小弟看了看,由于时间紧迫,所以一时半会也没有看明白太多,没能解决。恳请各位大侠指点指点,深表感谢!
...全文
1974 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
冰思雨 2011-09-16
  • 打赏
  • 举报
回复
缓冲区的字节数,已经被封装起来了,程序员一般情况下看不到。
最多调用available方法,来探测一下,下一次不受阻塞能够读取到的字节数。
其他,未知。不知道,楼主要缓冲区的字节数干什么用 。
pzx0524 2011-09-15
  • 打赏
  • 举报
回复
使用对象流就避免这个问题
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 soli11722984 的回复:]

服务器端如果想把数据传到客户端,其实只需要把数据写到buffer就好,并不是写到Socket里去

当客户端需要查看服务端的信息的时候,可以在客户端发一个请求过去,或者定时查询,到那时候,服务端其实就是把buffer里面的东西传过去

nio的Socket操作都不是对着Socket来操作的,都是对着buffer操作(应该说nio都是对buffer操作),然后当有请求过来的时候,由系统底层……
[/Quote]

你服务端不用 socket.write(buffer) 的?
potahai 2010-04-27
  • 打赏
  • 举报
回复
学习中。。。。。。。。
soli11722984 2010-04-27
  • 打赏
  • 举报
回复
服务器端如果想把数据传到客户端,其实只需要把数据写到buffer就好,并不是写到Socket里去

当客户端需要查看服务端的信息的时候,可以在客户端发一个请求过去,或者定时查询,到那时候,服务端其实就是把buffer里面的东西传过去

nio的Socket操作都不是对着Socket来操作的,都是对着buffer操作(应该说nio都是对buffer操作),然后当有请求过来的时候,由系统底层完成,所以说nio不阻塞,也就是因为这样
  • 打赏
  • 举报
回复
但是你这样写的话,如果服务端要往 Socket 里写些东西就不好弄了,呵呵
soli11722984 2010-04-27
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 bao110908 的回复:]
Java code
} else if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
while (true) {
readBuffer.clear();
int r = socketChannel.read(readBuf……
[/Quote]

selector.selectedKeys这句其实是阻塞语句,所以就算没请求进来,也不会一直循环

至于你说r<0的情况,那个只是测试一下而已,具体怎么写,要看需求
  • 打赏
  • 举报
回复
} else if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
while (true) {
readBuffer.clear();
int r = socketChannel.read(readBuffer);
System.out.println(r);
if (r <= 0) {
key.cancel();
socketChannel.close();
break;
}
System.out.println(new String(readBuffer.array()).trim());
// writeBuffer.flip();
// socketChannel.write(writeBuffer);
}
}


但是第一次在 isReadable() 为 true 的时候,就进入了 while(true) 死循环了,如果有数据读的话还好,但是如果没有数据读取的话就会在 read 那里阻塞掉。

另外,当 r < 0 的时候,表示客户端已经断开了连接,这时候才会退出。难道你的客户端用的是短连接?客户端要发消息时去连接一下,连完之后就断开了?
soli11722984 2010-04-27
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 bao110908 的回复:]
如果这样写的话,跟使用非阻塞通信没有区别,如果没有数据的话 read 会在那里阻塞。
[/Quote]
不会的,这样不会阻塞,那个是为了selector的循环
soli11722984 2010-04-27
  • 打赏
  • 举报
回复
那个应该需要吧,因为需要选择器来找对应的通道,而且那个while不是只为了一个通道服务,是为了所有连接过来的请求做服务
  • 打赏
  • 举报
回复
如果这样写的话,跟使用非阻塞通信没有区别,如果没有数据的话 read 会在那里阻塞。
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 soli11722984 的回复:]

引用 9 楼 bao110908 的回复:
7 楼的代码中,在 if (key.isReadable()) 中并不需要 while (true) 吧?


是说客户端还是服务端?
[/Quote]

服务端
soli11722984 2010-04-27
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 bao110908 的回复:]
7 楼的代码中,在 if (key.isReadable()) 中并不需要 while (true) 吧?
[/Quote]

是说客户端还是服务端?
  • 打赏
  • 举报
回复
7 楼的代码中,在 if (key.isReadable()) 中并不需要 while (true) 吧?
  • 打赏
  • 举报
回复
用 SocketChannel 并设置非阻塞模式的话,比使用 Socket 复杂多了。特别是操纵 ByteBuffer 这个缓冲区。

虽然 Socket 不如 SocketChannel 性能高,但是在开发上面简单很多。
  • 打赏
  • 举报
回复
数据是源源不断地传过来的,从何而知客户端会传多少数据过来?

当然了,一般会在双方约定的协议中的某几个字节会表示这一请求数据的长度。
soli11722984 2010-04-27
  • 打赏
  • 举报
回复
NIO的“通道”,都会有configureBlocking(boolean)这个方法,这个就是设置是否阻塞


package com.soli.nio;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class ServerNIO {
public static void main(String[] args) throws Exception {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
// ByteBuffer writeBuffer = ByteBuffer.wrap("服务器".getBytes());
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
writeBuffer.put("服务器".getBytes());

Selector selector = Selector.open();

InetSocketAddress address = new InetSocketAddress(9876);

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(address);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

System.out.println("start");

while (true) {
int temp = selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
System.out.println("keys.size()=" + temp);
if (temp > 0) {
for (Iterator<SelectionKey> it = keys.iterator(); it.hasNext();) {
SelectionKey key = it.next();
System.out.println(key);
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = ssc.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
while (true) {
readBuffer.clear();
int r = socketChannel.read(readBuffer);
System.out.println(r);
if (r <= 0) {
key.cancel();
socketChannel.close();
break;
}
System.out.println(new String(readBuffer.array()).trim());
// writeBuffer.flip();
// socketChannel.write(writeBuffer);
}
}
it.remove();
}
}
System.out.println("keys.size()=" + keys.size());
}
}
}




package com.soli.nio;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class ClientNIO {
public static void main(String[] args) throws Exception {
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
writeBuffer.put("客户端".getBytes());
// ByteBuffer writeBuffer = ByteBuffer.wrap("客户端".getBytes());
ByteBuffer readBuffer = ByteBuffer.allocate(1024);

Selector selector = Selector.open();

SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);

InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), 9876);
System.out.println(socketChannel.connect(address));

while (!socketChannel.finishConnect())
;
// socketChannel.register(selector, SelectionKey.OP_READ);

writeBuffer.flip();
socketChannel.write(writeBuffer);
// while (true) {
// int temp = selector.select();
// System.out.println(temp);
// if (temp > 0) {
// Set<SelectionKey> keys = selector.selectedKeys();
// for (Iterator<SelectionKey> it = keys.iterator(); it.hasNext();) {
// SelectionKey key = it.next();
// if (key.isReadable()) {
// System.out.println("ok");
// SocketChannel sc = (SocketChannel) key.channel();
// int r = sc.read(readBuffer);
// if (r <= 0) {
// break;
// }
// System.out.println(new String(readBuffer.array()).trim());
// }
// it.remove();
// }
// }
// }
}
}



上面写得有点混乱,而且没带注析,带注析的那份在家。。。。。。请见量
haizhixiang 2010-04-27
  • 打赏
  • 举报
回复
Buffer就是缓冲区的意思了,Socket通信就是读写相应的缓冲区了
haizhixiang 2010-04-27
  • 打赏
  • 举报
回复
谢谢大家的答复,我已经解决了read函数阻塞问题,使用Socket.SetSoTimeout,设置读取的超时时间,如果读到数据,则继续执行,否则抛出异常。

但是,关于如何统计缓冲区中的数据字节数,还一直没有找到方法,好像没有似的,奇怪的很,还请有知道的指点一二,谢谢!
taolei 2010-04-27
  • 打赏
  • 举报
回复
阻塞就阻塞吧,别玩NIO的非阻塞,那个东西比你现在要解决的问题复杂得多。
别为一个小问题制造更大的麻烦。
加载更多回复(6)

62,614

社区成员

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

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