50,545
社区成员
发帖
与我相关
我的任务
分享
首先是main函数:
public static void main(String[] args) {
int port = 8416;
int backlog = 500;
int acceptCount = 0;
Socket serverSocket = null;
try {
ServerSocket httpServer = new ServerSocket(port, backlog);
System.out.println("server:httpy");
while(true){
// 从等待队列中取出一个连接请求,建立socket连接
serverSocket = httpServer.accept();
acceptCount ++;
// 对每次客户端的请求都使用一个独立的线程来响应
HttpResponse response = new HttpResponse(serverSocket);
Thread thread = new Thread(response);
thread.start();
// 打出服务端已经接受并处理请求数
System.out.println(acceptCount);
}
} catch (IOException e) {
e.printStackTrace();
}
}
然后是服务端进行响应的线程类(HttpResponse):
class HttpResponse implements Runnable{
// 与客户端进行连接的socket
Socket serverSocket = null;
// 回车换行
String crlf = "\r\n";
// 服务端进行响应的输出流
BufferedWriter responseWriter = null;
BufferedReader br = null;
HttpResponse(Socket _socket){
serverSocket = _socket;
}
public void run(){
response();
}
/**
* 对客户端请求进行响应的处理方法
*
* @param none
* @return none
*/
public void response(){
try{
responseWriter =
new BufferedWriter(
new OutputStreamWriter(
serverSocket.getOutputStream()));
// 服务端进行响应的响应内容
StringBuffer content = new StringBuffer();
content.append("<html>");
content.append("<head>");
content.append("<title>");
content.append("httpy web server");
content.append("</title>");
content.append("</head>");
content.append("<body>");
content.append("<div>");
content.append("<a>server:httpy</a>");
content.append("</div>");
content.append("</body>");
content.append("</html>");
// 服务端进行响应的响应头
StringBuffer header = new StringBuffer();
header.append("HTTP/1.1 200 OK");
header.append(crlf);
header.append("Content-Length:");
header.append(content.toString().getBytes("utf-8").length);
header.append(crlf);
header.append("Content-Type:text/html;UTF-8");
header.append(crlf);
header.append(crlf);
// 输出响应头
responseWriter.write(header.toString());
// 输出响应内容
responseWriter.write(content.toString());
} catch (Exception e) {
e.printStackTrace();
}finally{
// 关闭资源连接
try {
if(responseWriter != null){
responseWriter.close();
}
if(serverSocket != null){
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
改成
String line = null;
while (!(line = reader.readLine()).isEmpty()) {
System.out.println(line);
}
当接收到空字符串的时候就退出等待
POST / HTTP/1.1
Content-Length: 0
Host: 192.168.1.83:8416
Connection: Keep-Alive
的时间节点是在读取服务端的response之前。
之后的第二部分又会block,这次block的release是在客户端的connection关闭后release.也就是在以下代码之后
if (httpPost != null) {
httpPost.releaseConnection();
}
if (httpClient != null) {
httpClient.getConnectionManager().shutdown();
}
这也就保证了服务端的socket in out的关闭是在客户端主动关闭以后服务端才释放资源的。
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
public class HttpClientTest {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(100);
final Runnable runnableImpl = new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName());
postCommunication("http://192.168.1.83:8416/");
}
};
for (int i = 0; i < 100; i++) {
service.submit(runnableImpl);
}
service.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
} finally {
if (service != null) {
service.shutdown();
}
}
}
public static final void postCommunication(final String url) {
DefaultHttpClient httpClient = null;
HttpPost httpPost = null;
String bodyMessage = null;
int statusCode = -1;
try {
// create instance httpClient with hTTP parameter
// set the retry handler
httpClient = new DefaultHttpClient(setParam());
httpClient
.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(
0, false));
// create post instance
httpPost = new HttpPost(url);
// httpPost.addHeader("Connection", "close");
// get the status code and body message.
HttpResponse response = httpClient.execute(httpPost);
statusCode = response.getStatusLine().getStatusCode();
HttpEntity reEntity = response.getEntity();
if (reEntity != null) {
bodyMessage = EntityUtils.toString(reEntity);
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (httpPost != null) {
httpPost.releaseConnection();
}
if (httpClient != null) {
httpClient.getConnectionManager().shutdown();
}
}
System.out.println("statusCode:" + statusCode + ",bodyMessage:"
+ bodyMessage);
}
private static final HttpParams setParam() {
final HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 2000);
HttpConnectionParams.setSoTimeout(httpParams, 2000);
HttpConnectionParams.setSocketBufferSize(httpParams, 1024 * 2);
HttpClientParams.setRedirecting(httpParams, true);
return httpParams;
}
}
以上的代码模拟100个用户并发执行100个http request.
但是经常会出现问题。
错误为以下
java.net.SocketException: Software caused connection abort: recv failed
这是什么原因呢,那我们就分析一下。
首先看一下出错在什么地方。
我们的代码错误在
HttpResponse response = httpClient.execute(httpPost);
到底在什么情况下出现这种问题,那就看一下httpclinet的源代码。
发现在这种情况下会出现错误。
1 server端
responseWriter.write(header.toString());
responseWriter.write(content.toString());
以后会立即关闭responseWriter
2 客户端httpclient在read response的时候根本还没有来得及read的时候,因为服务端已经主动关闭了。抛出了IOException
httpclient出错的具体位置
DefaultHttpResponseParser.java
method parseHead
int i = sessionBuffer.readLine(this.lineBuf); // 这里出错了,由于服务端的socketoutputstream已经关闭了 这里的sessionBuffer readline也就会抛出io exception.
所以说总结一下,出现这种错误的原因是因为当服务端向socket写数据的时候,由于没有等待客户端的读数据操作完成,就关闭了socket的outputstream和socket对象,导致客户端抛出了异常
下面说一下解决方法
解决方法 我想是不是可以在服务端写数据以后,有一个缓冲的时间,让客户端将数据读好呢。所以就先加了一个thread.sleep,这样测试下来,错误明显减少了不少,但是这并不能解决根本的问题
所以想当客户端将response读完以后,肯定对服务端有个回应才对,即告诉服务端我这边已经读取好了,你可以关闭了,所以在再服务端的代码中加入了对socket inputstream的读取,是加载write以后的。代码比较简单,如下
BufferedWriter responseWriter = null;
BufferedReader reader = null;
try {
responseWriter = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
StringBuffer content = new StringBuffer();
content.append("<html>");
content.append("<head>");
content.append("<title>");
content.append("httpy web server");
content.append("</title>");
content.append("</head>");
content.append("<body>");
content.append("<div>");
content.append("<a>server:httpy</a>");
content.append("</div>");
content.append("</body>");
content.append("</html>");
StringBuffer header = new StringBuffer();
header.append("HTTP/1.1 200 OK");
header.append(BR);
header.append("Content-Length:");
header.append(content.toString().getBytes("utf-8").length);
header.append(BR);
header.append("Content-Type:text/html;UTF-8");
header.append(BR);
header.append(BR);
responseWriter.write(header.toString());
responseWriter.write(content.toString());
// 这里的flush也是必须的。
responseWriter.flush();
// Thread.sleep(10);
// 这里等待客户端读取完response返回通知。
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (responseWriter != null) {
responseWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
综上所述,修改好以上的代码,1000线程做1000个http request也是没有什么问题的。代码可以优化的地方 thread的地方可以使用线程池,不需要一个request对应一个thread.只需要线程池就可以了。
public void response(){
try{
responseWriter =
new BufferedWriter(
new OutputStreamWriter(
serverSocket.getOutputStream()));
// 此处是新加入的代码片段$$$$$$$$$$$$$$$$$$$$$$$$
br =
new BufferedReader(
new OutputStreamReader(
serverSocket.getInputStream()));
br.read();
// 以上是新加入的代码片段$$$$$$$$$$$$$$$$$$$$$$$$
// 服务端进行响应的响应内容
StringBuffer content = new StringBuffer();
content.append("<html>");