我有一个多线程的Java套接字客户端,它将http请求发布到java http服务器。我正在寻找很少发生通信问题的原因,现在我发现在客户端两个线程在建立连接后得到相同的本地端口,并且在服务器端处理完成此请求时在本地端口上的一个线程获取连接重置错误(其他一个成功完成)。 在服务器端使用java.net.ServerSocket。 在客户端使用java.net.HttpURLConnection。
我的问题是两个不同的客户端在哪种情况下连接到同一个本地端口? 在客户端,每个客户端线程都有自己的java.net.HttpURLConnection,因此多线程不应该导致损坏。此外,当我在服务器以外的地方尝试此客户端时,不会发生此类错误。
服务器代码:
while (true) {
MySocket socket = null;
try {
socket = (MySocket)serverSocket.accept();
System.err.println("request received ");
socket.setTcpNoDelay(true);
Work work = createWork(socket);
try {
getWorkManager().scheduleWork(work, WorkManager.IMMEDIATE, null, null);
} catch (Throwable e) {
logger.error("Work not processed :: Local port: " + serverPort, e);
try {
socket.close();
} catch (Exception e2) {
}
}
}
catch (Throwable e) {
logger.error("Accept failed on socket:" + e.getMessage() +" Local Port : " + localPort + " Server Port : " + serverPort , e);
try {
if(socket != null)
socket.close();
} catch (Throwable ex) {
}
}
}
自定义ServerSocket能够写日志:
public class MyServerSocket extends ServerSocket {
public MyServerSocket(int i, int j) throws IOException {
super(i, j);
}
public Socket accept() throws IOException{
Socket socket = new MySocket();
implAccept(socket);
System.err.println("------->>>>socket localport opened :: " + socket.getPort());
return socket;
}
}
Custome Socket能够在close()上写日志:
public class MySocket extends Socket{
public MySocket() { super(); }
public MySocket(String host, int port)
throws IOException {
super(host, port);
}
public synchronized void close(){
System.err.println("------->>>>port closed :: "+getPort());
try {
super.close();
} catch (Exception e) {
System.err.println("------->>>>error on socket close :: "+e.getMessage());
}
}
客户端日志:
private void sendMultiThreaded() {
ExecutorService executor = Executors.newFixedThreadPool(5);
int counter = 0;
while (true) {
try {
Thread.currentThread().sleep(250);
executor.execute(new SenderThread(++counter));
executor.execute(new SenderThread(++counter));
} catch (Exception e) {
}
}
}
class SenderThread implements Runnable{
int counter;
public SenderThread(int counter ){
this.counter = counter;
}
@Override
public void run() {
try {
sendPost("SPMLOG_CALL_PROCEDURE~3092376387582~"+counter+"~3340003184406406~3917010108414~25~~24945169989630~25~ES~12433930256382~",counter);
} catch (Exception e) {
}
}
}
private void sendPost(String input, int counter) throws Exception {
String url = "http://localhost:7033/FBWS/eigBagHttpDispatcher";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
try {
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "CORE");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(input);
wr.flush();
wr.close();
System.out.println("\nSending 'POST' request to URL : " + url + "counter :: " + counter);
int responseCode =0;
try {
responseCode = con.getResponseCode();
} catch (Exception e) {
printPort(con, "EXCEPTION --Thread ID:"+ Thread.currentThread().getId());
System.err.println(e.getMessage() +" :: counter ::" +counter);
throw e;
}
System.out.println("Response Code : " + responseCode);
printPort(con, "NORMAL--Thread ID:"+ Thread.currentThread().getId());
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
System.out.println(response.toString());
}
finally{
if (con != null)
con.disconnect();
}
}
private void printPort(HttpURLConnection con, String message)throws Exception {
Field http = con.getClass().getDeclaredField("http");
http.setAccessible(true);
Field serverSocket = http.get(con).getClass().getSuperclass().getDeclaredField("serverSocket");
serverSocket.setAccessible(true);
Field impl = serverSocket.get(http.get(con)).getClass().getDeclaredField("impl");
impl.setAccessible(true);
Field localport = impl.get(serverSocket.get(http.get(con))).getClass().getSuperclass().getSuperclass().getDeclaredField("localport");
localport.setAccessible(true);
System.err.println(message + " -- local port for request is :: "+ localport.get(impl.get(serverSocket.get(http.get(con)))));
}
示例输出:
Server Side:
request received
------->>>>socket localport opened :: 18980
------->>>>port closed :: 18980
******
Client Side:
NORMAL --Thread ID: 14 -- local port for request is :: 18980
EXCEPTION --Thread ID:18 -- local port for request is :: 18980
Unexpected end of file from server :: counter ::4
java.net.SocketException: Unexpected end of file from server
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:790)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:653)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1207)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:390)
at com.kasim.coreclient.CoreClient.sendPost(CoreClient.java:74)
at com.kasim.coreclient.CoreClient.access$0(CoreClient.java:52)
at com.kasim.coreclient.CoreClient$SenderThread.run(CoreClient.java:44)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:897)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:919)
at java.lang.Thread.run(Thread.java:736)
Ps:我扩展了ServerSocket和Socket类,以便能够记录本地端口值。我和原来的课程有同样的错误。