大家好日子!我正在開發基於NIO的服務器,並試圖用簡單的客戶端程序對其進行測試。爲什麼我的非阻塞Java服務器拒絕客戶端連接?
在發佈代碼之前,我想簡單描述一下問題:在測試用例中,服務器立即執行他的工作一切正常。但是,當我試圖添加一些真實的生活行爲,如服務時間短,我得到「java.net.ConnectException:連接被拒絕」異常。更準確地說,100個客戶端線程的一部分會得到此連接拒絕異常。
我使用下面的代碼:
客戶
public class TCPClient implements Runnable{
private String name;
public TCPClient(String name)
{
this.name = name;
}
public static void main(String[] args)
{
for(int i=0;i<100;i++)
{
Thread t = new Thread(new TCPClient("thread # "+Integer.toString(i)));
t.start();
}
}
@Override
public void run()
{
Socket socket = null;
OutputStream out = null;
int counter = 0;
try
{
socket = new Socket();
socket.connect(new InetSocketAddress("192.168.3.109",2345), 0);
out = socket.getOutputStream();
byte[] bytes;
while(counter<100)
{
counter++;
bytes = (name+ ", message # "+Integer.toString(counter)+System.lineSeparator()).getBytes();
out.write(bytes);
out.flush();
Thread.sleep(200);
}
}
catch(Exception ex)
{
System.out.println(name+" "+Integer.toString(counter));
ex.printStackTrace(new PrintStream(System.out));
System.out.println();
}
finally
{
if(socket!=null && out!=null)
{
try
{
socket.close();
out.close();
}
catch(Exception ex)
{
System.out.println("client close error");
}
}
}
}
}
服務器
public class TCPServer {
private Selector selector;
private boolean isRunning;
private ServerSocketChannel server;
private int counter;
private PrintWriter times;
private PrintWriter logger;
private Charset charset;
private CharsetDecoder decoder;
ByteBuffer bb;
long serviceTime,curTime;
Random random;
public TCPServer(int port)
{
counter = 0;
isRunning = false;
serviceTime = 0;
random = new Random();
random.setSeed(System.currentTimeMillis());
bb = ByteBuffer.allocate(2048);
try
{
selector = Selector.open();
server = ServerSocketChannel.open();
server.socket().bind(new InetSocketAddress(port));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
}
catch(Exception ex)
{
System.out.println("initialization error "+ex.getMessage());
}
}
public void startServer() {
isRunning = true;
int acc = 0;
boolean error = false;
while (isRunning) {
try
{
selector.select();
Set keys = selector.selectedKeys();
Iterator it = keys.iterator();
while(it.hasNext())
{
SelectionKey key = (SelectionKey)it.next();
if (key.isConnectable())
{
((SocketChannel)key.channel()).finishConnect();
}
if (key.isAcceptable())
{
//logger.println("socket accepted");
//logger.flush();
acc++;
System.out.println("accepted sockets count = "+acc);
SocketChannel client = server.accept();
client.configureBlocking(false);
client.socket().setTcpNoDelay(true);
client.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable())
{
curTime = System.currentTimeMillis();
SocketChannel sc = (SocketChannel) key.channel();
bb.clear();
int x = sc.read(bb);
if(x==-1)
{
key.cancel();
continue;
}
counter++;
// Thread.sleep(2);
int sum=0;
for(int dummy=0;dummy<4000000;dummy++) // without this delay client works fine
{
sum+=random.nextInt();
sum%=1005;
}
serviceTime+= System.currentTimeMillis() - curTime;
if(counter>=10000)
{
System.out.println("recieved messages count = "+counter);
System.out.println("service time = "+serviceTime+" milliseconds");
}
}
}
keys.clear();
}
catch (Exception ex)
{
System.out.println("error in recieving messages "+ex.getMessage());
}
}
}
public static void main(String[] args)
{
TCPServer deviceServer = new TCPServer(2345);
deviceServer.startServer();
}
}
的問題是在(僞...)循環 - 這是服務的延遲只是模擬 - 解析傳入消息所需的時間,向DB寫入內容等等。當延遲很小時,代碼工作正常,所有10000條消息到達服務器(100個客戶端線程X 100個來自每個客戶端的消息),但當虛擬循環超過3.000.000次迭代時,某些客戶端線程無法連接到服務器。另一個奇怪的事情是忽略客戶套接字的無限超時屬性。我的意思是socket.connect(InetAddress,timeout)的timeout等於零意味着無限超時 - 換句話說服務延遲沒有意義,至少我期望這種行爲。
「連接拒絕」異常是否立即發生或在客戶端發生延遲後發生? – Uooo 2013-05-08 04:46:44
不立即。一些客戶端線程設法連接到服務器,其他客戶線程在短時間後會出現異常。 – Baurzhan 2013-05-08 04:54:41
什麼是「短時間之後」?幾秒,幾分鐘,幾毫秒? – Uooo 2013-05-08 05:05:41