socketconnection.java
来自「基于Jabber协议的即时消息服务器」· Java 代码 · 共 676 行 · 第 1/2 页
JAVA
676 行
/**
* $RCSfile$
* $Revision: 3187 $
* $Date: 2005-12-11 13:34:34 -0300 (Sun, 11 Dec 2005) $
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.wildfire.net;
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.*;
import org.jivesoftware.wildfire.auth.UnauthorizedException;
import org.jivesoftware.wildfire.interceptor.InterceptorManager;
import org.jivesoftware.wildfire.interceptor.PacketRejectedException;
import org.jivesoftware.wildfire.server.IncomingServerSession;
import org.xmpp.packet.Packet;
import javax.net.ssl.SSLSession;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.channels.Channels;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* An object to track the state of a XMPP client-server session.
* Currently this class contains the socket channel connecting the
* client and server.
*
* @author Iain Shigeoka
*/
public class SocketConnection implements Connection {
/**
* The utf-8 charset for decoding and encoding XMPP packet streams.
*/
public static final String CHARSET = "UTF-8";
private static Map<SocketConnection, String> instances =
new ConcurrentHashMap<SocketConnection, String>();
/**
* Milliseconds a connection has to be idle to be closed. Timeout is disabled by default. It's
* up to the connection's owner to configure the timeout value. Sending stanzas to the client
* is not considered as activity. We are only considering the connection active when the
* client sends some data or hearbeats (i.e. whitespaces) to the server.
* The reason for this is that sending data will fail if the connection is closed. And if
* the thread is blocked while sending data (because the socket is closed) then the clean up
* thread will close the socket anyway.
*/
private long idleTimeout = -1;
final private Map<ConnectionCloseListener, Object> listeners =
new HashMap<ConnectionCloseListener, Object>();
private Socket socket;
private SocketReader socketReader;
private Writer writer;
private AtomicBoolean writing = new AtomicBoolean(false);
/**
* Deliverer to use when the connection is closed or was closed when delivering
* a packet.
*/
private PacketDeliverer backupDeliverer;
private Session session;
private boolean secure;
private boolean compressed;
private org.jivesoftware.util.XMLWriter xmlSerializer;
private boolean flashClient = false;
private int majorVersion = 1;
private int minorVersion = 0;
private String language = null;
private TLSStreamHandler tlsStreamHandler;
private long writeStarted = -1;
/**
* TLS policy currently in use for this connection.
*/
private TLSPolicy tlsPolicy = TLSPolicy.optional;
/**
* Compression policy currently in use for this connection.
*/
private CompressionPolicy compressionPolicy = CompressionPolicy.disabled;
public static Collection<SocketConnection> getInstances() {
return instances.keySet();
}
/**
* Create a new session using the supplied socket.
*
* @param backupDeliverer the packet deliverer this connection will use when socket is closed.
* @param socket the socket to represent.
* @param isSecure true if this is a secure connection.
* @throws NullPointerException if the socket is null.
*/
public SocketConnection(PacketDeliverer backupDeliverer, Socket socket, boolean isSecure)
throws IOException {
if (socket == null) {
throw new NullPointerException("Socket channel must be non-null");
}
this.secure = isSecure;
this.socket = socket;
// DANIELE: Modify socket to use channel
if (socket.getChannel() != null) {
writer = Channels.newWriter(
ServerTrafficCounter.wrapWritableChannel(socket.getChannel()), CHARSET);
}
else {
writer = new BufferedWriter(new OutputStreamWriter(
ServerTrafficCounter.wrapOutputStream(socket.getOutputStream()), CHARSET));
}
this.backupDeliverer = backupDeliverer;
xmlSerializer = new XMLSocketWriter(writer, this);
instances.put(this, "");
}
/**
* Returns the stream handler responsible for securing the plain connection and providing
* the corresponding input and output streams.
*
* @return the stream handler responsible for securing the plain connection and providing
* the corresponding input and output streams.
*/
public TLSStreamHandler getTLSStreamHandler() {
return tlsStreamHandler;
}
/**
* Secures the plain connection by negotiating TLS with the client. When connecting
* to a remote server then <tt>clientMode</tt> will be <code>true</code> and
* <tt>remoteServer</tt> is the server name of the remote server. Otherwise <tt>clientMode</tt>
* will be <code>false</code> and <tt>remoteServer</tt> null.
*
* @param clientMode boolean indicating if this entity is a client or a server.
* @param remoteServer server name of the remote server we are connecting to or <tt>null</tt>
* when not in client mode.
* @throws IOException if an error occured while securing the connection.
*/
public void startTLS(boolean clientMode, String remoteServer) throws IOException {
if (!secure) {
secure = true;
// Prepare for TLS
tlsStreamHandler = new TLSStreamHandler(socket, clientMode, remoteServer, session instanceof IncomingServerSession);
if (!clientMode) {
// Indicate the client that the server is ready to negotiate TLS
deliverRawText("<proceed xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>");
}
// Start handshake
tlsStreamHandler.start();
// Use new wrapped writers
writer = new BufferedWriter(new OutputStreamWriter(tlsStreamHandler.getOutputStream(), CHARSET));
xmlSerializer = new XMLSocketWriter(writer, this);
}
}
/**
* Start using compression for this connection. Compression will only be available after TLS
* has been negotiated. This means that a connection can never be using compression before
* TLS. However, it is possible to use compression without TLS.
*
* @throws IOException if an error occured while starting compression.
*/
public void startCompression() throws IOException {
compressed = true;
if (tlsStreamHandler == null) {
ZOutputStream out = new ZOutputStream(
ServerTrafficCounter.wrapOutputStream(socket.getOutputStream()),
JZlib.Z_BEST_COMPRESSION);
out.setFlushMode(JZlib.Z_PARTIAL_FLUSH);
writer = new BufferedWriter(new OutputStreamWriter(out, CHARSET));
xmlSerializer = new XMLSocketWriter(writer, this);
}
else {
ZOutputStream out = new ZOutputStream(tlsStreamHandler.getOutputStream(), JZlib.Z_BEST_COMPRESSION);
out.setFlushMode(JZlib.Z_PARTIAL_FLUSH);
writer = new BufferedWriter(new OutputStreamWriter(out, CHARSET));
xmlSerializer = new XMLSocketWriter(writer, this);
}
}
public boolean validate() {
if (isClosed()) {
return false;
}
boolean allowedToWrite = false;
try {
requestWriting();
allowedToWrite = true;
// Register that we started sending data on the connection
writeStarted();
writer.write(" ");
writer.flush();
}
catch (Exception e) {
Log.warn("Closing no longer valid connection" + "\n" + this.toString(), e);
close();
}
finally {
// Register that we finished sending data on the connection
writeFinished();
if (allowedToWrite) {
releaseWriting();
}
}
return !isClosed();
}
public void init(Session owner) {
session = owner;
}
public Object registerCloseListener(ConnectionCloseListener listener, Object handbackMessage) {
Object status = null;
if (isClosed()) {
listener.onConnectionClose(handbackMessage);
}
else {
status = listeners.put(listener, handbackMessage);
}
return status;
}
public Object removeCloseListener(ConnectionCloseListener listener) {
return listeners.remove(listener);
}
public InetAddress getInetAddress() {
return socket.getInetAddress();
}
/**
* Returns the port that the connection uses.
*
* @return the port that the connection uses.
*/
public int getPort() {
return socket.getPort();
}
/**
* Returns the Writer used to send data to the connection. The writer should be
* used with caution. In the majority of cases, the {@link #deliver(Packet)}
* method should be used to send data instead of using the writer directly.
* You must synchronize on the writer before writing data to it to ensure
* data consistency:
*
* <pre>
* Writer writer = connection.getWriter();
* synchronized(writer) {
* // write data....
* }</pre>
*
* @return the Writer for this connection.
*/
public Writer getWriter() {
return writer;
}
public boolean isClosed() {
if (session == null) {
return socket.isClosed();
}
return session.getStatus() == Session.STATUS_CLOSED;
}
public boolean isSecure() {
return secure;
}
public boolean isCompressed() {
return compressed;
}
public TLSPolicy getTlsPolicy() {
return tlsPolicy;
}
/**
* Sets whether TLS is mandatory, optional or is disabled. When TLS is mandatory clients
* are required to secure their connections or otherwise their connections will be closed.
* On the other hand, when TLS is disabled clients are not allowed to secure their connections
* using TLS. Their connections will be closed if they try to secure the connection. in this
* last case.
*
* @param tlsPolicy whether TLS is mandatory, optional or is disabled.
*/
public void setTlsPolicy(TLSPolicy tlsPolicy) {
this.tlsPolicy = tlsPolicy;
}
public CompressionPolicy getCompressionPolicy() {
return compressionPolicy;
}
/**
* Sets whether compression is enabled or is disabled.
*
* @param compressionPolicy whether Compression is enabled or is disabled.
*/
public void setCompressionPolicy(CompressionPolicy compressionPolicy) {
this.compressionPolicy = compressionPolicy;
}
public long getIdleTimeout() {
return idleTimeout;
}
/**
* Sets the number of milliseconds a connection has to be idle to be closed. Sending
* stanzas to the client is not considered as activity. We are only considering the
* connection active when the client sends some data or hearbeats (i.e. whitespaces)
* to the server.
*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?