我修改了此处可用于Client和Server My客户端的示例代码:
public class Client {
public static void main(String[] args) {
int n=10000;
SocketTest [] st= new SocketTest[n];
for(int i=0;i<n;i++)
st[i]= new SocketTest("hi");
for(int i=0;i<n;i++)
new Thread(st[i]).start();
}
}
class SocketTest implements Runnable {
private String message = "";
private Selector selector;
private int i;
public SocketTest(String message){
this.message = message;
}
@Override
public void run() {
SocketChannel channel;
try {
selector = Selector.open();
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_CONNECT);
channel.connect(new InetSocketAddress("127.0.0.1", 8511));
while (!Thread.currentThread().isInterrupted()){
selector.select();
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()){
SelectionKey key = keys.next();
keys.remove();
if (!key.isValid()) continue;
if (key.isConnectable()){
connect(key);
System.out.println("I am connected to the server");
}
if (key.isWritable()){
write(key);
}
if (key.isReadable()){
read(key);
}
}
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} finally {
close();
}
}
private void close(){
try {
selector.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void read (SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1000);
readBuffer.clear();
int length;
try{
length = channel.read(readBuffer);
} catch (IOException e){
System.out.println("Reading problem, closing connection");
key.cancel();
channel.close();
return;
}
if (length == -1){
System.out.println("Nothing was read from server");
channel.close();
key.cancel();
return;
}
readBuffer.flip();
byte[] buff = new byte[1024];
readBuffer.get(buff, 0, length);
//length=buff.length;
String fromserver = new String(buff,0,length,"UTF-8");
length = fromserver.length();
System.out.println("Server said: "+fromserver);
key.interestOps(SelectionKey.OP_WRITE);
}
private void write(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
i++;
message = "location now "+i;
try{
Thread.sleep(5000);
}
catch(InterruptedException ie)
{
System.out.println(""+ie);
}
channel.write(ByteBuffer.wrap(message.getBytes()));
// lets get ready to read.
key.interestOps(SelectionKey.OP_READ);
}
private void connect(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
try
{
if(!channel.finishConnect())
System.out.println("* Here *");
}
catch(ConnectException e)
{
System.out.println("BP 1");
e.printStackTrace();
//channel.close();
//key.cancel();
//return;
}
/*if (channel.isConnectionPending()){
while(!channel.ffinishConnect()){
System.out.println("not connected");
}
}*/
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_WRITE);
}
}
Run Code Online (Sandbox Code Playgroud)
我通过创建多个线程在同一台机器上创建多个客户端.线程数不是由n的值决定的.当我运行少量客户端时,我遇到没有问题,但是一旦我用n运行500即500个客户端线程,一些线程正确运行但在某些情况下我遇到这个:
java.net.ConnectException: Connection refused: no further information
at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
at sun.nio.ch.SocketChannelImpl.finishConnect(Unknown Source)
at SocketTest.connect(Client.java:143)
at SocketTest.run(Client.java:61)
第143行是:
if(!channel.finishConnect())
所以当我阅读这个方法的文档时,它说它抛出:
NoConnectionPendingException - 如果未连接此通道且尚未启动连接操作.
ClosedChannelException - 如果此通道已关闭.
AsynchronousCloseException - 如果另一个线程在连接操作正在进行时关闭此通道.
ClosedByInterruptException - 如果另一个线程在连接操作正在进行时中断当前线程,从而关闭通道并设置当前线程的中断状态.
IOException - 如果发生其他一些I/O错误.
但Exception是ConnectException.我尝试捕捉它,但它没有进入catch块.
任何帮助将不胜感激.谢谢. 编辑: 我在Windows上工作.我尝试更改n的值,看看有多少客户端被创建,有多少导致异常,这些是结果(我知道等待更多时间,每次测试后将允许更多的开放套接字,因为每个测试scokets将在TIME_WAIT之后发布):
n clients connected(by keeping a count at server)
1000 522
2000 568
3000 626
4000 600 (maybe I gave less time before successive runs)
5000 1345
6000 1389
我很困惑,只有这么多客户才能连接起来.任何人都可以建议更好的参考阅读Client Server NIO.
编辑2
正如EJP在评论中提到的那样,Backlog Queue窗口已经满了.我修改了客户端代码以生成100个线程,然后休眠5秒,这样队列上没有太多负载,并且大部分连接都成功(但是当仍然有10,000个连接时,一些仍然失败).
ConnectException: connection refused意味着没有任何东西在您尝试连接的 IP:port 上侦听,或者在服务器的侦听积压队列已填满的某些平台上。如果它被抛出并且你正确地抓住它,你肯定会抓住它。您必须扩展实际发生的情况以及实际捕获代码的样子以获得进一步帮助。
但是,您还有许多其他问题:
private void connect(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
try
{
if(!channel.finishConnect())
System.out.println("* Here *");
Run Code Online (Sandbox Code Playgroud)
此时,如果finishConnect()返回false,则应返回。你应该不会落空,并重新注册为通道OP_WRITE.的连接仍悬而未决。打印"* Here *"也很无用。尝试打印一些有意义的东西。
}
catch(ConnectException e)
{
System.out.println("BP 1");
e.printStackTrace();
//channel.close();
Run Code Online (Sandbox Code Playgroud)
此时您当然应该关闭通道。它对人或兽没有进一步的用处。
//key.cancel();
Run Code Online (Sandbox Code Playgroud)
关闭通道会取消该键。在遇到的地方删除。
//return;
Run Code Online (Sandbox Code Playgroud)
如上所述,此时您当然应该返回。
}
/*if (channel.isConnectionPending()){
while(!channel.ffinishConnect()){
System.out.println("not connected");
}
}*/
Run Code Online (Sandbox Code Playgroud)
摆脱这个渣滓。在非阻塞模式下自旋循环是不合适的。甚至不要把它作为评论放在一边:一些白痴可能会在稍后出现并把它放回去。
channel.configureBlocking(false);
Run Code Online (Sandbox Code Playgroud)
通道已经处于非阻塞模式。否则你不会在这里。消除。
channel.register(selector, SelectionKey.OP_WRITE);
Run Code Online (Sandbox Code Playgroud)
另一种方法是 key.interestOps(SelectionKey.OP_WRITE);
沉睡在网络代码中实际上是在浪费时间。它不解决任何问题。
您假设write()完全成功,并且您忽略了它返回的计数。
您使用的是质量相当差的参考:
write()应用的相同评论与上述相同。flip() 不是“像重置”。ByteBuffer,但无论如何分配ByteBuffer每次读取都是不好的做法。ServerSocketChannel.accept() 可以返回 null.Map当键有附件时,不需要使用 a 。Thread.interrupted()无论如何,当 NIO 可中断时,无需继续测试。IOException一对一频道而关闭所有内容。尝试找到更好的东西。
| 归档时间: |
|
| 查看次数: |
13408 次 |
| 最近记录: |