Java 7 NIO2高性能Web服务器并发连接的处理

开发 后端
NIO.2是针对Java中I/O功能的一系列增强,计划在Java 7中发布。现在NIO.2功能已经可以在Java 7里程碑版本中使用。本文介绍NIO2的并发连接数的处理方法。

本文来自DoubleH的BlogJava博客,原文标题为《基于JDK7 NIO2的高性能web服务器实践之二》。该主题的第一篇博文可在这里阅读

51CTO推荐专题:Java 7 下一代Java开发技术详解

前一篇博客,我简单提了下怎么为NIO2增加TransmitFile支持,文件传送吞吐量是一个性能关注点,此外,并发连接数也是重要的关注点。

不过JDK7中又一次做了简单的实现,不支持同时投递多个AcceptEx请求,只支持一次一个,返回后再投递。这样,客户端连接的接受速度必然大打折扣。不知道为什么sun会做这样的实现,WSASend()/WSAReceive()一次只允许一个还是可以理解,毕竟简化了编程,不用考虑封包乱序问题。

也降低了内存耗尽的风险。AcceptEx却没有这样的理由了。

于是再一次为了性能,我增加了同时投递多个的支持。

另外,在JDK7的默认实现中,AcceptEx返回后,为了设置远程和本地InetSocketAddress也采用了效率很低的方法。4次通过JNI调用getsockname,2次为了取sockaddr,2次为了取port. 这些操作本人采用GetAcceptExSockaddrs一次完成,进一步提高效率。

先看Java部分的代码,框架跟JDK7的一样,细节处理不一样:

  1. /**  
  2.  *   
  3.  */ 
  4. package sun.nio.ch;  
  5.  
  6. import java.io.IOException;  
  7. import java.lang.reflect.Field;  
  8. import java.lang.reflect.Method;  
  9. import java.net.InetAddress;  
  10. import java.net.InetSocketAddress;  
  11. import java.nio.channels.AcceptPendingException;  
  12. import java.nio.channels.AsynchronousCloseException;  
  13. import java.nio.channels.AsynchronousServerSocketChannel;  
  14. import java.nio.channels.AsynchronousSocketChannel;  
  15. import java.nio.channels.ClosedChannelException;  
  16. import java.nio.channels.CompletionHandler;  
  17. import java.nio.channels.NotYetBoundException;  
  18. import java.nio.channels.ShutdownChannelGroupException;  
  19. import java.security.AccessControlContext;  
  20. import java.security.AccessController;  
  21. import java.security.PrivilegedAction;  
  22. import java.util.Queue;  
  23. import java.util.concurrent.ConcurrentLinkedQueue;  
  24. import java.util.concurrent.Future;  
  25. import java.util.concurrent.atomic.AtomicBoolean;  
  26. import java.util.concurrent.atomic.AtomicInteger;  
  27.  
  28. import sun.misc.Unsafe;  
  29.  
  30. /**  
  31.  * This class enable multiple 'AcceptEx' post on the completion port, hence improve the concurrent connection number.  
  32.  * @author Yvon  
  33.  *  
  34.  */ 
  35. public class WindowsMultiAcceptSupport {  
  36.  
  37.     WindowsAsynchronousServerSocketChannelImpl schannel;  
  38.  
  39.     private static final Unsafe unsafe = Unsafe.getUnsafe();  
  40.  
  41.     // 2 * (sizeof(SOCKET_ADDRESS) + 16)  
  42.     private static final int ONE_DATA_BUFFER_SIZE = 88;  
  43.  
  44.     private long handle;  
  45.     private Iocp iocp;  
  46.  
  47.     // typically there will be zero, or one I/O operations pending. In rare  
  48.     // cases there may be more. These rare cases arise when a sequence of accept  
  49.     // operations complete immediately and handled by the initiating thread.  
  50.     // The corresponding OVERLAPPED cannot be reused/released until the completion  
  51.     // event has been posted.  
  52.     private PendingIoCache ioCache;  
  53.  
  54.     private Queue<Long> dataBuffers;  
  55.     // the data buffer to receive the local/remote socket address  
  56.     //        private final long dataBuffer;  
  57.  
  58.     private AtomicInteger pendingAccept;  
  59.     private int maxPending;  
  60.  
  61.     Method updateAcceptContextM;  
  62.     Method acceptM;  
  63.  
  64.     WindowsMultiAcceptSupport() {  
  65.         //dummy for JNI code.  
  66.     }  
  67.  
  68.     public void close() throws IOException {  
  69.  
  70.         schannel.close();  
  71.  
  72.         for (int i = 0; i < maxPending + 1; i++)//assert there is maxPending+1 buffer in the queue  
  73.         {  
  74.             long addr = dataBuffers.poll();  
  75.             // release  resources  
  76.             unsafe.freeMemory(addr);  
  77.         }  
  78.  
  79.     }  
  80.  
  81.     /**  
  82.      *   
  83.      */ 
  84.     public WindowsMultiAcceptSupport(AsynchronousServerSocketChannel ch, int maxPost) {  
  85.         if (maxPost <= 0 || maxPost > 1024)  
  86.             throw new IllegalStateException("maxPost can't less than 1 and greater than 1024");  
  87.         this.schannel = (WindowsAsynchronousServerSocketChannelImpl) ch;  
  88.         maxPending = maxPost;  
  89.         dataBuffers = new ConcurrentLinkedQueue<Long>();  
  90.         for (int i = 0; i < maxPending + 1; i++) {  
  91.             dataBuffers.add(unsafe.allocateMemory(ONE_DATA_BUFFER_SIZE));  
  92.         }  
  93.  
  94.         pendingAccept = new AtomicInteger(0);  
  95.         try {  
  96.             Field f = WindowsAsynchronousServerSocketChannelImpl.class.getDeclaredField("handle");  
  97.             f.setAccessible(true);  
  98.             handle = f.getLong(schannel);  
  99.  
  100.  
  101.             f = WindowsAsynchronousServerSocketChannelImpl.class.getDeclaredField("iocp");  
  102.             f.setAccessible(true);  
  103.             iocp = (Iocp) f.get(schannel);  
  104.  
  105.             f = WindowsAsynchronousServerSocketChannelImpl.class.getDeclaredField("ioCache");  
  106.             f.setAccessible(true);  
  107.             ioCache = (PendingIoCache) f.get(schannel);  
  108.  
  109.             f = WindowsAsynchronousServerSocketChannelImpl.class.getDeclaredField("accepting");  
  110.             f.setAccessible(true);  
  111.             AtomicBoolean accepting = (AtomicBoolean) f.get(schannel);  
  112.  
  113.             accepting.set(true);//disable accepting by origin channel.  
  114.  
  115.         } catch (Exception e) {  
  116.             e.printStackTrace();  
  117.         }  
  118.  
  119.     }  
  120.  
  121.     @SuppressWarnings("unchecked")  
  122.     public final <A> void accept(A attachment,  
  123.         CompletionHandler<AsynchronousSocketChannel, ? super A> handler) {  
  124.         if (handler == null)  
  125.             throw new NullPointerException("'handler' is null");  
  126.         implAccept(attachment, (CompletionHandler<AsynchronousSocketChannel, Object>) handler);  
  127.     }  
  128.  
  129.     /**  
  130.      * Task to initiate accept operation and to handle result.  
  131.      */ 
  132.     private class AcceptTask implements Runnable, Iocp.ResultHandler {  
  133.  
  134.         private final WindowsAsynchronousSocketChannelImpl channel;  
  135.         private final AccessControlContext acc;  
  136.         private final PendingFuture<AsynchronousSocketChannel, Object> result;  
  137.         private final long dataBuffer;  
  138.  
  139.         AcceptTask(WindowsAsynchronousSocketChannelImpl channel, AccessControlContext acc,  
  140.             long dataBuffer, PendingFuture<AsynchronousSocketChannel, Object> result) {  
  141.             this.channel = channel;  
  142.             this.acc = acc;  
  143.             this.result = result;  
  144.             this.dataBuffer = dataBuffer;  
  145.         }  
  146.  
  147.         void enableAccept() {  
  148.             pendingAccept.decrementAndGet();  
  149.             dataBuffers.add(dataBuffer);  
  150.         }  
  151.  
  152.         void closeChildChannel() {  
  153.             try {  
  154.                 channel.close();  
  155.             } catch (IOException ignore) {  
  156.             }  
  157.         }  
  158.  
  159.         // caller must have acquired read lock for the listener and child channel.  
  160.         void finishAccept() throws IOException {  
  161.             /**  
  162.              * JDK7 use 4 calls to getsockname  to setup  
  163.              * local& remote address, this is very inefficient.  
  164.              *   
  165.              * I change this to use GetAcceptExSockaddrs  
  166.              */ 
  167.  
  168.             InetAddress[] socks = new InetAddress[2];  
  169.             int[] ports = new int[2];  
  170.             updateAcceptContext(handle, channel.handle(), socks, ports, dataBuffer);  
  171.             InetSocketAddress local = new InetSocketAddress(socks[0], ports[0]);  
  172.             final InetSocketAddress remote = new InetSocketAddress(socks[1], ports[1]);  
  173.             channel.setConnected(local, remote);  
  174.  
  175.             // permission check (in context of initiating thread)  
  176.             if (acc != null) {  
  177.                 AccessController.doPrivileged(new PrivilegedAction<Void>() {  
  178.  
  179.                     public Void run() {  
  180.                         SecurityManager sm = System.getSecurityManager();  
  181.                         sm.checkAccept(remote.getAddress().getHostAddress(), remote.getPort());  
  182.  
  183.                         return null;  
  184.                     }  
  185.                 }, acc);  
  186.             }  
  187.         }  
  188.  
  189.         /**  
  190.          * Initiates the accept operation.  
  191.          */ 
  192.         @Override 
  193.         public void run() {  
  194.             long overlapped = 0L;  
  195.  
  196.             try {  
  197.                 // begin usage of listener socket  
  198.                 schannel.begin();  
  199.                 try {  
  200.                     // begin usage of child socket (as it is registered with  
  201.                     // completion port and so may be closed in the event that  
  202.                     // the group is forcefully closed).  
  203.                     channel.begin();  
  204.  
  205.                     synchronized (result) {  
  206.                         overlapped = ioCache.add(result);  
  207.  
  208.                         
  209.                         int n = accept0(handle, channel.handle(), overlapped, dataBuffer);//Be careful for the buffer address  
  210.                         if (n == IOStatus.UNAVAILABLE) {  
  211.                             return;  
  212.                         }  
  213.  
  214.                         // connection accepted immediately  
  215.                         finishAccept();  
  216.  
  217.                         // allow another accept before the result is set  
  218.                         enableAccept();  
  219.                         result.setResult(channel);  
  220.                     }  
  221.                 } finally {  
  222.                     // end usage on child socket  
  223.                     channel.end();  
  224.                 }  
  225.             } catch (Throwable x) {  
  226.                 // failed to initiate accept so release resources  
  227.                 if (overlapped != 0L)  
  228.                     ioCache.remove(overlapped);  
  229.                 closeChildChannel();  
  230.                 if (x instanceof ClosedChannelException)  
  231.                     x = new AsynchronousCloseException();  
  232.                 if (!(x instanceof IOException) && !(x instanceof SecurityException))  
  233.                     x = new IOException(x);  
  234.                 enableAccept();  
  235.                 result.setFailure(x);  
  236.             } finally {  
  237.                 // end of usage of listener socket  
  238.                 schannel.end();  
  239.             }  
  240.  
  241.             // accept completed immediately but may not have executed on  
  242.             // initiating thread in which case the operation may have been  
  243.             // cancelled.  
  244.             if (result.isCancelled()) {  
  245.                 closeChildChannel();  
  246.             }  
  247.  
  248.             // invoke completion handler  
  249.             Invoker.invokeIndirectly(result);  
  250.         }  
  251.  
  252.         /**  
  253.          * Executed when the I/O has completed  
  254.          */ 
  255.         @Override 
  256.         public void completed(int bytesTransferred, boolean canInvokeDirect) {  
  257.             try {  
  258.                 // connection accept after group has shutdown  
  259.                 if (iocp.isShutdown()) {  
  260.                     throw new IOException(new ShutdownChannelGroupException());  
  261.                 }  
  262.  
  263.                 // finish the accept  
  264.                 try {  
  265.                     schannel.begin();  
  266.                     try {  
  267.                         channel.begin();  
  268.                         finishAccept();  
  269.                     } finally {  
  270.                         channel.end();  
  271.                     }  
  272.                 } finally {  
  273.                     schannel.end();  
  274.                 }  
  275.  
  276.                 // allow another accept before the result is set  
  277.                 enableAccept();  
  278.                 result.setResult(channel);  
  279.             } catch (Throwable x) {  
  280.                 enableAccept();  
  281.                 closeChildChannel();  
  282.                 if (x instanceof ClosedChannelException)  
  283.                     x = new AsynchronousCloseException();  
  284.                 if (!(x instanceof IOException) && !(x instanceof SecurityException))  
  285.                     x = new IOException(x);  
  286.                 result.setFailure(x);  
  287.             }  
  288.  
  289.             // if an async cancel has already cancelled the operation then  
  290.             // close the new channel so as to free resources  
  291.             if (result.isCancelled()) {  
  292.                 closeChildChannel();  
  293.             }  
  294.  
  295.             // invoke handler (but not directly)  
  296.             Invoker.invokeIndirectly(result);  
  297.         }  
  298.  
  299.         @Override 
  300.         public void failed(int error, IOException x) {  
  301.             enableAccept();  
  302.             closeChildChannel();  
  303.  
  304.             // release waiters  
  305.             if (schannel.isOpen()) {  
  306.                 result.setFailure(x);  
  307.             } else {  
  308.                 result.setFailure(new AsynchronousCloseException());  
  309.             }  
  310.             Invoker.invokeIndirectly(result);  
  311.         }  
  312.     }  
  313.  
  314.     Future<AsynchronousSocketChannel> implAccept(Object attachment,  
  315.         final CompletionHandler<AsynchronousSocketChannel, Object> handler) {  
  316.         if (!schannel.isOpen()) {  
  317.             Throwable exc = new ClosedChannelException();  
  318.             if (handler == null)  
  319.                 return CompletedFuture.withFailure(exc);  
  320.             Invoker.invokeIndirectly(schannel, handler, attachment, null, exc);  
  321.             return null;  
  322.         }  
  323.         if (schannel.isAcceptKilled())  
  324.             throw new RuntimeException("Accept not allowed due to cancellation");  
  325.  
  326.         // ensure channel is bound to local address  
  327.         if (schannel.localAddress == null)  
  328.             throw new NotYetBoundException();  
  329.  
  330.         // create the socket that will be accepted. The creation of the socket  
  331.         // is enclosed by a begin/end for the listener socket to ensure that  
  332.         // we check that the listener is open and also to prevent the I/O  
  333.         // port from being closed as the new socket is registered.  
  334.         WindowsAsynchronousSocketChannelImpl ch = null;  
  335.         IOException ioe = null;  
  336.         try {  
  337.             schannel.begin();  
  338.             ch = new WindowsAsynchronousSocketChannelImpl(iocp, false);  
  339.         } catch (IOException x) {  
  340.             ioe = x;  
  341.         } finally {  
  342.             schannel.end();  
  343.         }  
  344.         if (ioe != null) {  
  345.             if (handler == null)  
  346.                 return CompletedFuture.withFailure(ioe);  
  347.             Invoker.invokeIndirectly(this.schannel, handler, attachment, null, ioe);  
  348.             return null;  
  349.         }  
  350.  
  351.         // need calling context when there is security manager as  
  352.         // permission check may be done in a different thread without  
  353.         // any application call frames on the stack  
  354.         AccessControlContext acc =  
  355.             (System.getSecurityManager() == null) ? null : AccessController.getContext();  
  356.  
  357.         PendingFuture<AsynchronousSocketChannel, Object> result =  
  358.             new PendingFuture<AsynchronousSocketChannel, Object>(schannel, handler, attachment);  
  359.  
  360.         // check and set flag to prevent concurrent accepting  
  361.         if (pendingAccept.get() >= maxPending)  
  362.             throw new AcceptPendingException();  
  363.         pendingAccept.incrementAndGet();  
  364.         AcceptTask task = new AcceptTask(ch, acc, dataBuffers.poll(), result);  
  365.         result.setContext(task);  
  366.  
  367.         // initiate I/O  
  368.         if (Iocp.supportsThreadAgnosticIo()) {  
  369.             task.run();  
  370.         } else {  
  371.             Invoker.invokeOnThreadInThreadPool(this.schannel, task);  
  372.         }  
  373.         return result;  
  374.     }  
  375.  
  376.     //    //reimplements for performance  
  377.     static native void updateAcceptContext(long listenSocket, long acceptSocket,  
  378.         InetAddress[] addresses, int[] ports, long dataBuffer) throws IOException;  
  379.  
  380.     static native int accept0(long handle, long handle2, long overlapped, long dataBuffer);  
  381.  
  382. }  
  383.  

对应的CPP代码如下:

  1. /*  
  2.  * Class:     sun_nio_ch_WindowsMultiAcceptSupport  
  3.  * Method:    updateAcceptContext  
  4.  * Signature: (JJ[Ljava/net/InetAddress;[IJ)V  
  5.  */ 
  6. JNIEXPORT void JNICALL Java_sun_nio_ch_WindowsMultiAcceptSupport_updateAcceptContext  
  7. (JNIEnv *env , jclass clazz, jlong listenSocket, jlong acceptSocket, jobjectArray sockArray,jintArray portArray,jlong buf)  
  8. {  
  9.     SOCKET s1 = (SOCKET)jlong_to_ptr(listenSocket);  
  10.     SOCKET s2 = (SOCKET)jlong_to_ptr(acceptSocket);  
  11.     PVOID outputBuffer = (PVOID)jlong_to_ptr(buf);  
  12.     INT iLocalAddrLen=0;  
  13.     INT iRemoteAddrLen=0;  
  14.     SOCKETADDRESS* lpLocalAddr;  
  15.     SOCKETADDRESS* lpRemoteAddr;  
  16.     jobject localAddr;  
  17.     jobject remoteAddr;  
  18.     jint ports[2]={0};  
  19.  
  20.       
  21.  
  22.     setsockopt(s2, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&s1, sizeof(s1));  
  23.  
  24.     (lpGetAcceptExSockaddrs)(outputBuffer,  
  25.         0,  
  26.         sizeof(SOCKETADDRESS)+16,  
  27.         sizeof(SOCKETADDRESS)+16,  
  28.         (LPSOCKADDR*)&lpLocalAddr,  
  29.         &iLocalAddrLen,  
  30.         (LPSOCKADDR*)&lpRemoteAddr,  
  31.         &iRemoteAddrLen);  
  32.  
  33.     localAddr=lpNET_SockaddrToInetAddress(env,(struct sockaddr *)lpLocalAddr,(int *)ports);  
  34.     remoteAddr=lpNET_SockaddrToInetAddress(env,(struct sockaddr *)lpRemoteAddr,(int *)(ports+1));  
  35.  
  36.     env->SetObjectArrayElement(sockArray,0,localAddr);  
  37.     env->SetObjectArrayElement(sockArray,1,remoteAddr);  
  38.     env->SetIntArrayRegion(portArray,0,2,ports);  
  39.  
  40. }  
  41.  
  42. /*  
  43.  * Class:     sun_nio_ch_WindowsMultiAcceptSupport  
  44.  * Method:    accept0  
  45.  * Signature: (JJJJ)I  
  46.  */ 
  47. jint JNICALL Java_sun_nio_ch_WindowsMultiAcceptSupport_accept0  
  48.   (JNIEnv *env, jclass clazz, jlong listenSocket, jlong acceptSocket, jlong ov, jlong buf)  
  49. {  
  50.  
  51.     BOOL res;  
  52.     SOCKET s1 = (SOCKET)jlong_to_ptr(listenSocket);  
  53.     SOCKET s2 = (SOCKET)jlong_to_ptr(acceptSocket);  
  54.     PVOID outputBuffer = (PVOID)jlong_to_ptr(buf);  
  55.  
  56.     DWORD nread = 0;  
  57.     OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov);  
  58.     ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED));  
  59.  
  60.       
  61.  
  62.     //why use SOCKETADDRESS?  
  63.     //because client may use IPv6 to connect to server.  
  64.     res = (lpAcceptEx)(s1,  
  65.         s2,  
  66.         outputBuffer,  
  67.         0,  
  68.         sizeof(SOCKETADDRESS)+16,  
  69.         sizeof(SOCKETADDRESS)+16,  
  70.         &nread,  
  71.         lpOverlapped);  
  72.  
  73.       
  74.     if (res == 0) {  
  75.         int error = WSAGetLastError();  
  76.           
  77.         if (error == ERROR_IO_PENDING) {  
  78.               
  79.             return NIO2_IOS_UNAVAILABLE;  
  80.         }  
  81.       
  82.       
  83.         return NIO2_THROWN;  
  84.     }  
  85.  
  86.  
  87.  
  88.       
  89.     return 0;  
  90.  
  91. }  
  92.  

这里用到的lpNET_SockaddrToInetAddress是JDK7中NET.DLL暴露的方法,从DLL里加载。相应代码如下:

  1. *  
  2.  * Class:     com_yovn_jabhttpd_utilities_SunPackageFixer  
  3.  * Method:    initFds  
  4.  * Signature: ()V  
  5.  */  
  6. JNIEXPORT void JNICALL Java_com_yovn_jabhttpd_utilities_SunPackageFixer_initFds  
  7.   (JNIEnv *env, jclass clazz)  
  8. {  
  9.  
  10.  
  11.     GUID GuidAcceptEx = WSAID_ACCEPTEX;  
  12.     GUID GuidTransmitFile = WSAID_TRANSMITFILE;  
  13.     GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;  
  14.     SOCKET s;  
  15.     int rv;  
  16.     DWORD dwBytes;  
  17.     HMODULE hModule;  
  18.  
  19.  
  20.     s = socket(AF_INET, SOCK_STREAM, 0);  
  21.     if (s == INVALID_SOCKET) {  
  22.         JNU_ThrowByName(env,"java/io/IOException""socket failed");  
  23.         return;  
  24.     }  
  25.     rv = WSAIoctl(s,  
  26.         SIO_GET_EXTENSION_FUNCTION_POINTER,  
  27.         (LPVOID)&GuidAcceptEx,  
  28.         sizeof(GuidAcceptEx),  
  29.         &lpAcceptEx,  
  30.         sizeof(lpAcceptEx),  
  31.         &dwBytes,  
  32.         NULL,  
  33.         NULL);  
  34.     if (rv != 0)  
  35.     {  
  36.         JNU_ThrowByName(env, "java/io/IOException","WSAIoctl failed on get AcceptEx ");  
  37.         goto _ret;  
  38.     }  
  39.     rv = WSAIoctl(s,  
  40.         SIO_GET_EXTENSION_FUNCTION_POINTER,  
  41.         (LPVOID)&GuidTransmitFile,  
  42.         sizeof(GuidTransmitFile),  
  43.         &lpTransmitFile,  
  44.         sizeof(lpTransmitFile),  
  45.         &dwBytes,  
  46.         NULL,  
  47.         NULL);  
  48.     if (rv != 0)  
  49.     {  
  50.         JNU_ThrowByName(env, "java/io/IOException","WSAIoctl failed on get TransmitFile");  
  51.         goto _ret;  
  52.     }  
  53.     rv = WSAIoctl(s,  
  54.         SIO_GET_EXTENSION_FUNCTION_POINTER,  
  55.         (LPVOID)&GuidGetAcceptExSockAddrs,  
  56.         sizeof(GuidGetAcceptExSockAddrs),  
  57.         &lpGetAcceptExSockaddrs,  
  58.         sizeof(lpGetAcceptExSockaddrs),  
  59.         &dwBytes,  
  60.         NULL,  
  61.         NULL);  
  62.     if (rv != 0)  
  63.     {  
  64.         JNU_ThrowByName(env, "java/io/IOException","WSAIoctl failed on get GetAcceptExSockaddrs");  
  65.         goto _ret;  
  66.     }  
  67.  
  68.     hModule=LoadLibrary("net.dll");  
  69.     if(hModule==NULL)  
  70.     {  
  71.         JNU_ThrowByName(env, "java/io/IOException","can't load java net.dll");  
  72.         goto _ret;  
  73.     }  
  74.  
  75.  
  76.     lpNET_SockaddrToInetAddress=(NET_SockaddrToInetAddress_t)GetProcAddress(hModule,"_NET_SockaddrToInetAddress@12");  
  77.  
  78.     if(lpNET_SockaddrToInetAddress==NULL)  
  79.     {  
  80.         JNU_ThrowByName(env, "java/io/IOException","can't resolve _NET_SockaddrToInetAddress function ");  
  81.           
  82.           
  83.     }  
  84.  
  85. _ret:  
  86.     closesocket(s);  
  87.     return;  
  88.  
  89.  
  90. }  
  91.  

#T#细心的同学可能会发现,在创建socket之前没有初始化WinSock库,因为在这段代码前,我初始化了一个InetSocketAddress对象,这样JVM会加载NET.DLL并初始化WinSock库了。

OK,现在,你可以在支持类上同时发起多个AcceptEx请求了。

PS:基于这个我简单测试了下我的服务器,同时开5000个线程,每个下载3M多点的文件,一分钟内能够全部正确完成。

服务器正在开发中,有兴趣的请加入http://code.google.com/p/jabhttpd

责任编辑:yangsai 来源: BlogJava
相关推荐

2009-11-30 09:40:23

Java 7 NIO2HTTP Server

2011-03-11 09:51:47

Java NIO

2019-07-31 14:36:46

Linux服务器框架

2011-04-07 13:39:24

WebHTTP

2020-11-10 07:46:09

服务器高并发高性能

2011-12-15 11:39:25

JavaNIO

2012-11-08 09:57:29

低功耗高性能服务器处理器

2009-11-17 14:05:57

微软高性能计算服务器

2012-10-19 09:38:32

英特尔高性能微型服务器

2011-11-01 09:41:54

Nginx

2009-02-18 12:45:00

2010-05-07 17:50:31

Unix服务器

2023-10-18 07:52:48

Nginx静态资源服务器

2009-02-09 18:02:00

2021-05-12 23:07:16

服务器处理连接

2021-09-22 16:25:17

服务器戴尔科技集团

2014-04-09 10:50:01

Squid架构缓存服务器

2009-02-12 00:00:00

博科8Gb HBA主机总线适配器

2018-10-18 10:17:40

服务器并发多线程

2011-12-08 10:12:34

JavaNIO
点赞
收藏

51CTO技术栈公众号