【发布时间】:2016-04-03 05:58:33
【问题描述】:
我们有一个 Java Ldap 客户端,它创建一个连接并绑定到 AD(Active Directory)。此连接保持开放以供将来使用。空闲超时(15 分钟)后,AD 通过发送 TCP RST 关闭连接。以后使用这样的 conn 时,ldap 操作将按预期失败。在此类失败时,我们会明确关闭 ldap 连接。但是这些套接字句柄没有被释放,最终在 lsof 输出中处于 无法识别协议 状态。 strace 表示在此类失败的 ldap 句柄上显式 close 不会导致套接字关闭系统调用。
在某些情况下,AD 通过发送 FIN 关闭空闲连接。在这种情况下,LDAP 库本身会完全关闭 conn,并且不会发生此问题。
这是 Java Ldap 库中的错误吗?有什么解决办法吗?
测试代码重现问题
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.InitialLdapContext;
public class Ldap {
public static DirContext connect(String host, String port, String bindDn, String password) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
String ldapUrl = "ldap://" + host + ":" + port;
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapUrl);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, bindDn);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.REFERRAL, "ignore");
return new InitialLdapContext(env, null);
}
public static void main(String[] args) {
try {
System.out.println("Ldap connect....");
DirContext conn = connect("ipaddress", "389", "user@domain.com", "password");
System.out.println("Sleep - active ldap conn");
Thread.sleep(18 * 60 * 1000);
System.out.println("Closing conn...");
conn.close();
System.out.println("Sleep for ever...");
Thread.sleep(60 * 10 * 60 * 1000);
} catch(Exception e) {
e.printStackTrace();
}
}
}
Ldap 线程(RST)
recvfrom(5, 0x7ffd27274540, 8192, 0, 0, 0) = -1 ECONNRESET (Connection reset by peer)
lseek(3, 43442010, SEEK_SET) = 43442010
read(3, "PK\3\4\n\0\0\0\0\0\321\205\222E\241\375N\256\204\1\0\0\204\1\0\0&\0\0\0", 30) = 30
lseek(3, 43442078, SEEK_SET) = 43442078
read(3, "\312\376\272\276\0\0\0003\0\25\1\0\3()V\1\0\25(Ljava/lang/S"..., 388) = 388
recvfrom(5, "", 8192, 0, NULL, NULL) = 0
sendto(5, "0\5\2\1\2B\0", 7, 0, NULL, 0) = -1 EPIPE (Broken pipe)
--- SIGPIPE (Broken pipe) @ 0 (0) ---
rt_sigreturn(0xd) = -1 EPIPE (Broken pipe)
sendto(5, "0\5\2\1\2B\0", 7, 0, NULL, 0) = -1 EPIPE (Broken pipe)
--- SIGPIPE (Broken pipe) @ 0 (0) ---
rt_sigreturn(0xd) = -1 EPIPE (Broken pipe)
mmap(0x7ffd27185000, 12288, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7ffd27185000
rt_sigprocmask(SIG_SETMASK, [QUIT], NULL, 8) = 0
madvise(0x7ffd27185000, 1028096, MADV_DONTNEED) = 0
_exit(0) = ?
Process 47983 detached
Ldap 线程(FIN)
recvfrom(5, "", 8192, 0, NULL, NULL) = 0
gettimeofday({1451376987, 341537}, NULL) = 0
sendto(5, "0\5\2\1\2B\0", 7, 0, NULL, 0) = 7
dup2(4, 5) = 5
close(5) = 0
mmap(0x7fc3f8984000, 12288, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7fc3f8984000
gettimeofday({1451376987, 342129}, NULL) = 0
rt_sigprocmask(SIG_SETMASK, [QUIT], NULL, 8) = 0
madvise(0x7fc3f8984000, 1028096, MADV_DONTNEED) = 0
_exit(0)
【问题讨论】:
-
如果您想做严肃的 LDAP 工作,我建议您使用 LDAP SDK。 UnboundID 是我的最爱。
-
@jwilleke 我一直在读这篇文章,但我从来没有看到一个很好的理由。我正在做一些非常复杂的 LDAP 工作,所有这些都在 JNDI 中加上一些手写控件。如果 JNDI 支持全套 LDAP 控件,甚至是草稿控件,那就太好了,而且我有一个 RFE,但我不知道特殊用途的 SDK 可以解决的其他任何问题。
-
UnboundID Java SDK 将改变您的生活。由于它们是内置的,因此您将不再需要执行许多功能。例如,这些是“ServerSets”DNSSRVRecordServerSet、FailoverServerSet、FastestConnectServerSet、FewestConnectionsServerSet、RoundRobinDNSServerSet、RoundRobinServerSet、SingleServerSet,它们允许许多不同类型的池化和负载平衡能力。 docs.ldap.com/ldap-sdk/docs/javadoc/index.html.
标签: java active-directory ldap