📄 socketconnection.java
字号:
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.
*
* @param timeout the number of milliseconds a connection has to be idle to be closed.
*/
public void setIdleTimeout(long timeout) {
this.idleTimeout = timeout;
}
public int getMajorXMPPVersion() {
return majorVersion;
}
public int getMinorXMPPVersion() {
return minorVersion;
}
/**
* Sets the XMPP version information. In most cases, the version should be "1.0".
* However, older clients using the "Jabber" protocol do not set a version. In that
* case, the version is "0.0".
*
* @param majorVersion the major version.
* @param minorVersion the minor version.
*/
public void setXMPPVersion(int majorVersion, int minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
public String getLanguage() {
return language;
}
/**
* Sets the language code that should be used for this connection (e.g. "en").
*
* @param language the language code.
*/
public void setLanaguage(String language) {
this.language = language;
}
public boolean isFlashClient() {
return flashClient;
}
/**
* Sets whether the connected client is a flash client. Flash clients need to
* receive a special character (i.e. \0) at the end of each xml packet. Flash
* clients may send the character \0 in incoming packets and may start a
* connection using another openning tag such as: "flash:client".
*
* @param flashClient true if the if the connection is a flash client.
*/
public void setFlashClient(boolean flashClient) {
this.flashClient = flashClient;
}
public SSLSession getSSLSession() {
if (tlsStreamHandler != null) {
return tlsStreamHandler.getSSLSession();
}
return null;
}
public PacketDeliverer getPacketDeliverer() {
return backupDeliverer;
}
public void close() {
boolean wasClosed = false;
synchronized (this) {
if (!isClosed()) {
try {
if (session != null) {
session.setStatus(Session.STATUS_CLOSED);
}
boolean allowedToWrite = false;
try {
requestWriting();
allowedToWrite = true;
// Register that we started sending data on the connection
writeStarted();
writer.write("</stream:stream>");
if (flashClient) {
writer.write('\0');
}
writer.flush();
}
catch (IOException e) {
// Do nothing
}
finally {
// Register that we finished sending data on the connection
writeFinished();
if (allowedToWrite) {
releaseWriting();
}
}
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.close")
+ "\n" + this.toString(), e);
}
closeConnection();
wasClosed = true;
}
}
if (wasClosed) {
notifyCloseListeners();
}
}
public void systemShutdown() {
deliverRawText("<stream:error><system-shutdown " +
"xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error>");
close();
}
void writeStarted() {
writeStarted = System.currentTimeMillis();
}
void writeFinished() {
writeStarted = -1;
}
/**
* Returns true if the socket was closed due to a bad health. The socket is considered to
* be in a bad state if a thread has been writing for a while and the write operation has
* not finished in a long time or when the client has not sent a heartbeat for a long time.
* In any of both cases the socket will be closed.
*
* @return true if the socket was closed due to a bad health.s
*/
boolean checkHealth() {
// Check that the sending operation is still active
long writeTimestamp = writeStarted;
if (writeTimestamp > -1 && System.currentTimeMillis() - writeTimestamp >
JiveGlobals.getIntProperty("xmpp.session.sending-limit", 60000)) {
// Close the socket
if (Log.isDebugEnabled()) {
Log.debug("Closing connection: " + this + " that started sending data at: " +
new Date(writeTimestamp));
}
forceClose();
return true;
}
else {
// Check if the connection has been idle. A connection is considered idle if the client
// has not been receiving data for a period. Sending data to the client is not
// considered as activity.
if (idleTimeout > -1 && socketReader != null &&
System.currentTimeMillis() - socketReader.getLastActive() > idleTimeout) {
// Close the socket
if (Log.isDebugEnabled()) {
Log.debug("Closing connection that has been idle: " + this);
}
forceClose();
return true;
}
}
return false;
}
private void release() {
writeStarted = -1;
instances.remove(this);
}
/**
* Forces the connection to be closed immediately no matter if closing the socket takes
* a long time. This method should only be called from {@link SocketSendingTracker} when
* sending data over the socket has taken a long time and we need to close the socket, discard
* the connection and its session.
*/
private void forceClose() {
if (session != null) {
// Set that the session is closed. This will prevent threads from trying to
// deliver packets to this session thus preventing future locks.
session.setStatus(Session.STATUS_CLOSED);
}
closeConnection();
// Notify the close listeners so that the SessionManager can send unavailable
// presences if required.
notifyCloseListeners();
}
private void closeConnection() {
release();
try {
if (tlsStreamHandler == null) {
socket.close();
}
else {
// Close the channels since we are using TLS (i.e. NIO). If the channels implement
// the InterruptibleChannel interface then any other thread that was blocked in
// an I/O operation will be interrupted and an exception thrown
tlsStreamHandler.close();
}
}
catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error.close")
+ "\n" + this.toString(), e);
}
}
public void deliver(Packet packet) throws UnauthorizedException, PacketException {
if (isClosed()) {
backupDeliverer.deliver(packet);
}
else {
boolean errorDelivering = false;
boolean allowedToWrite = false;
try {
requestWriting();
allowedToWrite = true;
xmlSerializer.write(packet.getElement());
if (flashClient) {
writer.write('\0');
}
xmlSerializer.flush();
}
catch (Exception e) {
Log.debug("Error delivering packet" + "\n" + this.toString(), e);
errorDelivering = true;
}
finally {
if (allowedToWrite) {
releaseWriting();
}
}
if (errorDelivering) {
close();
// Retry sending the packet again. Most probably if the packet is a
// Message it will be stored offline
backupDeliverer.deliver(packet);
}
else {
session.incrementServerPacketCount();
}
}
}
public void deliverRawText(String text) {
if (!isClosed()) {
boolean errorDelivering = false;
boolean allowedToWrite = false;
try {
requestWriting();
allowedToWrite = true;
// Register that we started sending data on the connection
writeStarted();
writer.write(text);
if (flashClient) {
writer.write('\0');
}
writer.flush();
}
catch (Exception e) {
Log.debug("Error delivering raw text" + "\n" + this.toString(), e);
errorDelivering = true;
}
finally {
// Register that we finished sending data on the connection
writeFinished();
if (allowedToWrite) {
releaseWriting();
}
}
if (errorDelivering) {
close();
}
}
}
/**
* Notifies all close listeners that the connection has been closed.
* Used by subclasses to properly finish closing the connection.
*/
private void notifyCloseListeners() {
synchronized (listeners) {
for (ConnectionCloseListener listener : listeners.keySet()) {
try {
listener.onConnectionClose(listeners.get(listener));
}
catch (Exception e) {
Log.error("Error notifying listener: " + listener, e);
}
}
}
}
private void requestWriting() throws Exception {
for (;;) {
if (writing.compareAndSet(false, true)) {
// We are now in writing mode and only we can write to the socket
return;
}
else {
// Check health of the socket
if (checkHealth()) {
// Connection was closed then stop
throw new Exception("Probable dead connection was closed");
}
else {
Thread.sleep(1);
}
}
}
}
private void releaseWriting() {
writing.compareAndSet(true, false);
}
public String toString() {
return super.toString() + " socket: " + socket + " session: " + session;
}
public void setSocketReader(SocketReader socketReader) {
this.socketReader = socketReader;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -