📄 sharedsocket.java
字号:
// jTDS JDBC Driver for Microsoft SQL Server and Sybase
// Copyright (C) 2004 The jTDS Project
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
package net.sourceforge.jtds.jdbc;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedList;
import net.sourceforge.jtds.ssl.SocketFactories;
import net.sourceforge.jtds.util.Logger;
/**
* This class mananges the physical connection to the SQL Server and
* serialises its use amongst a number of virtual sockets.
* This allows one physical connection to service a number of concurrent
* statements.
* <p>
* Constraints and assumptions:
* <ol>
* <li>Callers will not attempt to read from the server without issuing a request first.
* <li>The end of a server reply can be identified as byte 2 of the header is non zero.
* </ol>
* </p>
* Comments:
* <ol>
* <li>This code will discard unread server data if a new request is issued.
* Currently the higher levels of the driver attempt to do this but may be
* we can just rely on this code instead.
* <li>A cancel can be issued by a caller only if the server is currently sending
* data for the caller otherwise the cancel is ignored.
* <li>Cancel packets on their own are returned as extra records appended to the
* previous packet so that the TdsCore module can process them.
* </ol>
* This version of the class will start to cache results to disk once a predetermined
* maximum buffer memory threshold has been passed. Small result sets that will fit
* within a specified limit (default 8 packets) will continue to be held in memory
* (even if the memory threshold has been passed) in the interests of efficiency.
*
* @author Mike Hutchinson.
* @version $Id: SharedSocket.java,v 1.39 2007/07/08 21:38:13 bheineman Exp $
*/
class SharedSocket {
/**
* This inner class contains the state information for the virtual socket.
*/
private static class VirtualSocket {
/**
* The stream ID of the stream objects owning this state.
*/
final int owner;
/**
* Memory resident packet queue.
*/
final LinkedList pktQueue;
/**
* True to discard network data.
*/
boolean flushInput;
/**
* True if output is complete TDS packet.
*/
boolean complete;
/**
* File object for disk packet queue.
*/
File queueFile;
/**
* I/O Stream for disk packet queue.
*/
RandomAccessFile diskQueue;
/**
* Number of packets cached to disk.
*/
int pktsOnDisk;
/**
* Total of input packets in memory or disk.
*/
int inputPkts;
/**
* Constuct object to hold state information for each caller.
* @param streamId the Response/Request stream id.
*/
VirtualSocket(int streamId) {
this.owner = streamId;
this.pktQueue = new LinkedList();
this.flushInput = false;
this.complete = false;
this.queueFile = null;
this.diskQueue = null;
this.pktsOnDisk = 0;
this.inputPkts = 0;
}
}
/**
* The shared network socket.
*/
private Socket socket;
/**
* The shared SSL network socket;
*/
private Socket sslSocket;
/**
* Output stream for network socket.
*/
private DataOutputStream out;
/**
* Input stream for network socket.
*/
private DataInputStream in;
/**
* Current maxium input buffer size.
*/
private int maxBufSize = TdsCore.MIN_PKT_SIZE;
/**
* Table of stream objects sharing this socket.
*/
private final ArrayList socketTable = new ArrayList();
/**
* The Stream ID of the object that is expecting a response from the server.
*/
private int responseOwner = -1;
/**
* Buffer for packet header.
*/
private final byte hdrBuf[] = new byte[TDS_HDR_LEN];
/**
* The directory to buffer data to.
*/
private final File bufferDir;
/**
* Total memory usage in all instances of the driver
* NB. Access to this field should probably be synchronized
* but in practice lost updates will not matter much and I think
* all VMs tend to do atomic saves to integer variables.
*/
private static int globalMemUsage;
/**
* Peak memory usage for debug purposes.
*/
private static int peakMemUsage;
/**
* Max memory limit to use for buffers.
* Only when this limit is exceeded will the driver
* start caching to disk.
*/
private static int memoryBudget = 100000; // 100K
/**
* Minimum number of packets that will be cached in memory
* before the driver tries to write to disk even if
* memoryBudget has been exceeded.
*/
private static int minMemPkts = 8;
/**
* Global flag to indicate that security constraints mean
* that attempts to create work files will fail.
*/
private static boolean securityViolation;
/**
* Tds protocol version
*/
private int tdsVersion;
/**
* The servertype one of Driver.SQLSERVER or Driver.SYBASE
*/
protected final int serverType;
/**
* The character set to use for converting strings to/from bytes.
*/
private CharsetInfo charsetInfo;
/**
* Count of packets received.
*/
private int packetCount;
/**
* The server host name.
*/
private String host;
/**
* The server port number.
*/
private int port;
/**
* A cancel packet is pending.
*/
private boolean cancelPending;
/**
* Synchronization monitor for {@link #cancelPending} and
* {@link #responseOwner}.
*/
private Object cancelMonitor = new Object();
/**
* Buffer for TDS_DONE packets
*/
private byte doneBuffer[] = new byte[TDS_DONE_LEN];
/**
* TDS done token.
*/
private static final int TDS_DONE_TOKEN = 253;
/**
* Length of a TDS_DONE token.
*/
private static final int TDS_DONE_LEN = 9;
/**
* Length of TDS packet header.
*/
private static final int TDS_HDR_LEN = 8;
protected SharedSocket(File bufferDir, int tdsVersion, int serverType) {
this.bufferDir = bufferDir;
this.tdsVersion = tdsVersion;
this.serverType = serverType;
}
/**
* Construct a <code>SharedSocket</code> object specifying host name and
* port.
*
* @param connection the connection object
* @throws IOException if socket open fails
*/
SharedSocket(ConnectionJDBC2 connection) throws IOException, UnknownHostException {
this(connection.getBufferDir(), connection.getTdsVersion(), connection.getServerType());
this.host = connection.getServerName();
this.port = connection.getPortNumber();
if (Driver.JDBC3) {
this.socket = createSocketForJDBC3(connection);
} else {
this.socket = new Socket(this.host, this.port);
}
setOut(new DataOutputStream(socket.getOutputStream()));
setIn(new DataInputStream(socket.getInputStream()));
this.socket.setTcpNoDelay(connection.getTcpNoDelay());
this.socket.setSoTimeout(connection.getSocketTimeout() * 1000);
}
/**
* Creates a {@link Socket} through reflection when {@link Driver#JDBC3}
* is <code>true</code>. Reflection must be used to stay compatible
* with JDK 1.3.
*
* @param connection the connection object
* @return a socket open to the host and port with the given timeout
* @throws IOException if socket open fails
*/
private Socket createSocketForJDBC3(ConnectionJDBC2 connection) throws IOException {
final String host = connection.getServerName();
final int port = connection.getPortNumber();
final int loginTimeout = connection.getLoginTimeout();
final String bindAddress = connection.getBindAddress();
try {
// Create the Socket
Constructor socketConstructor =
Socket.class.getConstructor(new Class[] {});
Socket socket =
(Socket) socketConstructor.newInstance(new Object[] {});
// Create the InetSocketAddress
Constructor constructor = Class.forName("java.net.InetSocketAddress")
.getConstructor(new Class[] {String.class, int.class});
Object address = constructor.newInstance(
new Object[] {host, new Integer(port)});
// Call Socket.bind(SocketAddress) if bindAddress parameter is set
if (bindAddress != null && bindAddress.length() > 0) {
Object localBindAddress = constructor.newInstance(
new Object[]{bindAddress, new Integer(0)});
Method bind = Socket.class.getMethod(
"bind", new Class[]{Class.forName("java.net.SocketAddress")});
bind.invoke(socket, new Object[]{localBindAddress});
}
// Call Socket.connect(InetSocketAddress, int)
Method connect = Socket.class.getMethod("connect", new Class[]
{Class.forName("java.net.SocketAddress"), int.class});
connect.invoke(socket,
new Object[] {address, new Integer(loginTimeout * 1000)});
return socket;
} catch (InvocationTargetException ite) {
// Reflection was OK but invocation of socket.bind() or socket.connect()
// has failed. Try to report the underlying reason
Throwable cause = ite.getTargetException();
if (cause instanceof IOException) {
// OK was an IOException or subclass so just throw it
throw (IOException) cause;
}
// Something else so return invocation exception anyway
// (This should not normally occur)
throw (IOException) Support.linkException(
new IOException("Could not create socket"), cause);
} catch (Exception e) {
// Reflection has failed for some reason e.g. security so
// try to create a socket in the old way.
return new Socket(host, port);
}
}
/**
* Enable TLS encryption by creating a TLS socket over the
* existing TCP/IP network socket.
*
* @param ssl the SSL URL property value
* @throws IOException if an I/O error occurs
*/
void enableEncryption(String ssl) throws IOException {
Logger.println("Enabling TLS encryption");
sslSocket = SocketFactories.getSocketFactory(ssl, socket)
.createSocket(getHost(), getPort());
setOut(new DataOutputStream(sslSocket.getOutputStream()));
setIn(new DataInputStream(sslSocket.getInputStream()));
}
/**
* Disable TLS encryption and switch back to raw TCP/IP socket.
*
* @throws IOException if an I/O error occurs
*/
void disableEncryption() throws IOException {
Logger.println("Disabling TLS encryption");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -