我想使用套接字用Java编写一个简单的http代理服务器。我写了一个测试原型,由我在网上找到的几个教程组成。这么久以来,我一直带着这个:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class SimpleProxyServer
{
public static final int portNumber = 55558;
public static final int maxConnections = 100;
public static void main( String[] args )
{
SimpleProxyServer proxyServer = new SimpleProxyServer();
proxyServer.start();
}
public void start()
{
System.out.println("Starting the SimpleProxyServer ...");
try
{
ServerSocket serverSocket = new ServerSocket( portNumber, maxConnections );
byte[] buffer = new byte[10000];
boolean run = true;
while( run )
{
Socket clientSocket = serverSocket.accept();
InputStream clientInputStream = clientSocket.getInputStream();
// reading the request and put it into buffer
final int readBytesCount = clientInputStream.read( buffer );
if( readBytesCount < 0)
continue;
String browserRequest = new String( buffer, 0, readBytesCount );
System.out.println( browserRequest );
// extract the host to connect to
final int hostNameStart = browserRequest.indexOf( "Host: " ) + 6;
final int hostNameEnd = browserRequest.indexOf( 'n', hostNameStart );
final String hostName = browserRequest.substring( hostNameStart, hostNameEnd - 1 );
System.out.println( "Connecting to host " + hostName );
// forward the response from the proxy to the server
Socket hostSocket = new Socket( hostName, 80 );
OutputStream hostOutputStream = hostSocket.getOutputStream();
System.out.println( "Forwarding request to server" );
hostOutputStream.write( buffer, 0, readBytesCount );
hostOutputStream.flush();
ProxyThread thread1 = new ProxyThread( clientSocket, hostSocket );
thread1.start();
ProxyThread thread2 = new ProxyThread( hostSocket, clientSocket );
thread2.start();
}
serverSocket.close();
}
catch( IOException e )
{
System.err.println( "IO Error: " + e.getMessage() );
e.printStackTrace();
}
}
}
class ProxyThread extends Thread
{
private Socket incoming, outgoing;
ProxyThread( Socket in, Socket out )
{
incoming = in;
outgoing = out;
}
// Overwritten run() method of thread,
// does the data transfers
public void run()
{
System.out.println( "Starting proxy thread" );
try
{
OutputStream toClient = outgoing.getOutputStream();
InputStream fromClient = incoming.getInputStream();
int numberRead = 0;
byte[] buffer = new byte[10000];
do
{
numberRead = fromClient.read( buffer );
System.out.println( "Read " + numberRead + " bytes" );
System.out.println( "Buffer: " + buffer );
if( numberRead > 0 )
{
toClient.write( buffer, 0, numberRead );
System.out.println( "Sent " + numberRead + " bytes" );
}
}
while( numberRead > 0 );
System.out.println( "Closing all streams and sockets" );
toClient.flush();
incoming.close();
outgoing.close();
}
catch( IOException e )
{
System.err.println( "IO Error: " + e.getMessage() );
}
catch( ArrayIndexOutOfBoundsException e )
{
System.err.println( "Index error: " + e.getMessage() );
}
}
}
在使用浏览器(如果Firefox有帮助的话)进行测试时,它会在fromClient.read(buffer)
调用上挂起并冻结很长一段时间,然后返回-1,但浏览器会更早地显示连接拒绝。是什么原因造成的?这是由InputStream.read()
阻塞引起的还是有点像比赛?也许整个方法是错误的?或者是线的问题?
p.S.我的"母语"是C++。尽管我偶尔会对Java编程语言有一些经验,但我对它的libs,尤其是套接字,仍然没有足够的经验。
p.p.S.我以为http代理服务器在互联网上有多个教程和操作方法,但我发现的只是单线程或不读取目标URL,只将请求发送给下一个代理。所以,也许这篇帖子会帮助某人写下自己的一篇。如果它被修复了。。。
您必须在一个单独的线程中处理每个接受的套接字,这包括读取CONNECT命令。否则,读取它的阻塞将阻止接受线程接受新的连接。