最近在用 Java的Socket框架mina开发一个服务器端程序,服务器写完后,用mina的客户端API写了程序去连接服务器端,也连接成功了,但是发现每次客户端连接完成运行后 过1分钟客户端程序才结束! 经过调试发现代码已经执行了客户端代码的最后一行! 真是奇怪,客户端代码如下:
public class Client
{
private static final String HOSTNAME = "localhost";
private static final int PORT = 20000;public static void main( String[] args ) throws Throwable { SocketConnector connector = new SocketConnector(); // Configure the service. SocketConnectorConfig cfg = new SocketConnectorConfig(); cfg.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) ); cfg.getFilterChain().addLast( "logger", new LoggingFilter() ); IoSession session; try { ConnectFuture future = connector.connect(new InetSocketAddress( HOSTNAME, PORT), new ClientSessionHandler("Hi hi :("), cfg); future.join(); session = future.getSession(); } catch (RuntimeIOException e) { System.err.println("Failed to connect."); e.printStackTrace(); } // wait until the summation is done session.getCloseFuture().join(); System.out.println(session,"### all done! !"); }
ExecutorService executor = Executors.newFixedThreadPool(1000);IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.setHandler( new TimeServerHandler() ); acceptor.getSessionConfig().setReadBufferSize( 2048 ); acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 ); acceptor.getFilterChain().addLast("executor", new ExecutorFilter(executor)); acceptor.getFilterChain().addLast( "logger", new LoggingFilter() ); acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) ); acceptor.bind( new InetSocketAddress(PORT) );</pre>
客户端很简单,就是模拟1000个并发连接:public static void main( String[] args ) throws Throwable
{
for(int i=0;i<1000;i++){
log.info("Enter run l..."+i);
new Thread(new ClientThread(i)).start();
}} </pre>
每个客户端代码是:public void run() {
// TODO Auto-generated method stub
log.info("#### thread :"+this.num+" Running...");
NioSocketConnector connector = new NioSocketConnector();
// Configure the service.connector.setConnectTimeoutMillis(CONNECT_TIMEOUT); connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new ObjectSerializationCodecFactory() ) ); connector.getFilterChain().addLast( "logger", new LoggingFilter() ); connector.setHandler(new ClientSessionHandler(" hihi new!")); IoSession session; for (;;) { try { ConnectFuture future = connector.connect(new InetSocketAddress( HOSTNAME, PORT)); future.awaitUninterruptibly(); session = future.getSession(); break; } catch (Exception e) { System.err.println("Failed to connect."); e.printStackTrace(); } } // wait until the summation is done session.getCloseFuture().awaitUninterruptibly(); connector.dispose(); log.info("#### thread :"+this.num+" over ..."); }</pre>
不知道这样写是不是已经用了NIO特性和数量是1000的连接池了. 而且我发现客户端如果不用mina的API,直接用Socket API连接,服务器撑不到1000个,200多个就Connection refused了.
问题补充
刚才试验了一下,模拟1000个客户端连接,成功了931个,异常是:2008-12-28 02:18:06,968 ERROR (LoggingFilter.java:127) - EXCEPTION :
java.io.IOException: 您的主机中的软件放弃了一个已建立的连接。
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:25)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:233)
at sun.nio.ch.IOUtil.read(IOUtil.java:206)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:207)
at org.apache.mina.transport.socket.nio.NioProcessor.read(NioProcessor.java:180)
at org.apache.mina.transport.socket.nio.NioProcessor.read(NioProcessor.java:42)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.read(AbstractPollingIoProcessor.java:568)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:547)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:539)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.access$400(AbstractPollingIoProcessor.java:57)
at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:867)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:65)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
at java.lang.Thread.run(Thread.java:595)
不知道调整哪些参数可以不发生这个错误? 就是说调整什么可以使1000个连接都能连上
问题补充
windows需要修改OS的一个设置,但是设置了也不起作用,我再试试吧! java的NIO的目的是使服务器端打开尽量少的Socket连接,能处理尽可能多的客户端请求(不知道主要是非阻塞队列起作用还是那个concurrent线程池在发挥作用,应该是前者),这样理解没错吧?
mina 1.1.x版本貌似是 session.close();
对应的方法定义是: CloseFuture close();
mina 2.0更简单好用。不过我们不是直接用。是参考2.0做了些裁减。
mina的性能没什么可担心的,成功案例有:openfire
可以google一下:openfire connection manager
如果你用的是mina 2.0.0-M4,那么你是漏了一行代码:
[code]
connector.dispose();
[/code]
其它版本的mina对应调用方法类似,可能叫close方法。
只要额外启动了线程而线程还没执行完,且程序没有调用System的exit,则程进程序会一直存在。
例如启动了swing的时候因为有了后台线程需要显式的调用System的exit才能退出,
或者就是使用了ThreadPoolExecutor之类的线程池而没有调用shutdown或shutdownNow,导致看上去代码执行到了最后一行而程序没有退出。
以及其它启动了类似run方法写成 while(true)之类的线程。
而楼主的一分钟退出,是因为socket被服务端超时断开了,客户端异步的线程很仔细完毕才让程序自动退出。
这个和“Java真是一个不稳定的东西”扯不上半点关系。
相关例子在 mina 的 example中:
[code]
closeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
client.quit();
connector.dispose();
dispose();
}
});
setLoggedOut();
setDefaultCloseOperation(EXIT_ON_CLOSE);
[/code]
asyncWeb虽然被整合到mina,但是好像作者没时间更新asyncweb 的代码,asyncweb 好像没法用新版的mina的jar,我很久没关注这个了。
2.0的api变化较大,写法和1.0不一样,你参考官方代码吧。
服务器撑不到连接应该是服务器的限制,linux是有打开文件的最大个数限制的,貌似windows也有,我没了解windows的。
引用:
修改linux最大连接数
1、直接用ulimit命令
ulimit -n 8192
2、修改/proc/sys/net/ipv4/ip_conntrack_max为8192
或者是/etc/sysctl.conf中加入ip_conntrack_max=8192
3、请首先编辑/usr/include/bits/types.h 文件,改变__FD_SETSIZE 的值:
#define _ _FD_SETSIZE 8192
下一步,使用这个命令增加内核文件描述符的限制:
最后,增加进程文件描述符的限制,在你即将编译squid 的同一个shell 里执行:
sh# ulimit -Hn 8192
该命令必须以root 运行,仅仅运行在bash shell。不必重启机器。
Socket连接是不可能少的,少了就是断开连接了。
nio是异步读取Socket流中和异步写入数据。
避免了线程浪费在读写的等待上面。
就像酒店的服务员,聪明的服务员会让你先看菜单,等你想好点什么了再叫他来点菜,
而不聪明的服务员,是从你一进店就守着你,直到你点好菜,像这种服务员一天服务不了多少顾客。
问下 做什么应用时要用到 mina 2.0呀~~
是啊,我也想知道什么应用需要MINA呢?难道是SE方面的应用?
[quote]最近在用 Java的Socket框架mina开发一个服务器端程序,服务器写完后,用mina的客户端API写了程序去连接服务器端,也连接成功了,[b]但是发现每次客户端连接完成运行后 过1分钟客户端程序才结束![/b] 经过调试发现代码已经执行了客户端代码的最后一行![/quote]
这个是因为SocketConnector会启动一个worker线程用来监听网络上的事件。每秒中轮询一次,默认的超时时间就是1分钟。可以通过setWorkerTimeout(int workerTimeout)来设置这个超时时间。
[code="java"] /**
* Set how many seconds the connection worker thread should remain alive once idle before terminating itself.
*
* @param workerTimeout the number of seconds to keep thread alive.
* Must be >=0. If 0 is specified, the connection
* worker thread will terminate immediately when
* there's no connection to make.
*/
public void setWorkerTimeout(int workerTimeout) {
if (workerTimeout < 0) {
throw new IllegalArgumentException("Must be >= 0");
}
this.workerTimeout = workerTimeout;
}[/code]
[code="java"] private class Worker implements Runnable {
private long lastActive = System.currentTimeMillis();
public void run() {
Selector selector = SocketConnector.this.selector;
for (;;) {
try {
int nKeys = selector.select(1000);
registerNew();
if (nKeys > 0) {
processSessions(selector.selectedKeys());
}
processTimedOutSessions(selector.keys());
if (selector.keys().isEmpty()) {
//检查是否超时
if (System.currentTimeMillis() - lastActive > workerTimeout * 1000L) {
synchronized (lock) {
if (selector.keys().isEmpty()
&& connectQueue.isEmpty()) {
worker = null;
try {
selector.close();
} catch (IOException e) {
ExceptionMonitor.getInstance()
.exceptionCaught(e);
} finally {
SocketConnector.this.selector = null;
}
break;
}
}
}
} else {
lastActive = System.currentTimeMillis();
}
} catch (IOException e) {
ExceptionMonitor.getInstance().exceptionCaught(e);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
ExceptionMonitor.getInstance().exceptionCaught(e1);
}
}
}
}
}[/code]
[quote="lyo"]刚才试验了一下,模拟1000个客户端连接,成功了931个,异常是:
[code]2008-12-28 02:18:06,968 ERROR (LoggingFilter.java:127) - EXCEPTION :
java.io.IOException: 您的主机中的软件放弃了一个已建立的连接。
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:25)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:233)
at sun.nio.ch.IOUtil.read(IOUtil.java:206)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:207)
at org.apache.mina.transport.socket.nio.NioProcessor.read(NioProcessor.java:180)
at org.apache.mina.transport.socket.nio.NioProcessor.read(NioProcessor.java:42)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.read(AbstractPollingIoProcessor.java:568)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:547)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:539)
at org.apache.mina.core.polling.AbstractPollingIoProcessor.access$400(AbstractPollingIoProcessor.java:57)
at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:867)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:65)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:650)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:675)
at java.lang.Thread.run(Thread.java:595)[/code]
不知道调整哪些参数可以不发生这个错误? 就是说调整什么可以使1000个连接都能连上[/quote]
这个异常是连接被断开了。你可以设置一下mina服务端的backlog,设置一个较大的值。
你是在什么操作系统上运行的,如果是64位solaris的话,单个进程打开文件最多是65536个,所以足够用,不需要修改。如果是32位或者是windows,默认值是多少我记不清了,你可以查一下,你要建立的连接的数量不能超过操作系统的限制。
[quote="codeutil"]mina 1.1.x版本貌似是 session.close();
对应的方法定义是: CloseFuture close();
[b]
mina 2.0更简单好用。不过我们不是直接用。是参考2.0做了些裁减。[/b]
mina的性能没什么可担心的,成功案例有:openfire
可以google一下:openfire connection manager
[/quote]
mina2.0 unstable,所以一直都没敢用,也没看有什么不同,听说是简化了接口。你们做了哪些裁剪?对mina的读写算法和bytebuffer的控制是否做了什么优化吗?
另外问一下,
比如像短信网关这种应用,即发送一个请求出去,然后得到一个响应,消息都不长,大概几十个字节。像这种应用,setReceiveBufferSize和setSendBufferSize是应该设置的较大一点好,还是较小一点好呢?
[quote]"问下 做什么应用时要用到 mina 2.0呀~~ "[/quote]
什么应用用短连接的socket,比如http server什么的,又想能支持c10k这个连接数的,长连接client数量有限传统io每连接每线程的模型应该够用,所以这种情况也没必要非用nio,不过自己有能力写还是自己写好
[quote]比如像短信网关这种应用,即发送一个请求出去,然后得到一个响应,消息都不长,大概几十个字节。像这种应用,setReceiveBufferSize和setSendBufferSize是应该设置的较大一点好,还是较小一点好呢? [/quote]
sendBuffer的作用是已发送数据在发送端的副本,主要用来出错/超时重传
receiveBuffer是已接受的数据存放的地方,然后依次从内核空间拷贝到应用
两个都应该设大点好吧,理论上最大是65535-TCP头-IP头,不过不同的网络不一样
mina我已经在生产环境中使用了,效果非常好,目前没有发现大的问题。
你们用SocketChannel获得socket的时候如果设置了timeout
难道都没碰到file descriptor leak的问题?
没有用那个方法 ,用了框架,你还用得到socket干什么呢?
[quote="wanghelong"]没有用那个方法 ,用了框架,你还用得到socket干什么呢?[/quote]
用的whalin 的MemCached的client
他使用了SocketChannel来获得socket
结果在jdk1.5,1.6,red hat下出现了pipe泄露