本实例采用生产者/消费者模式进行设计,源码中包含了详细的注释。为了方便测试将服务器放在一个Servlet里面启动,浏览器访问该Servlet时启动服务器监听
1. 服务器源码
package test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
//此处使用Servlet方式进行编写,方便进行测试,在应用启动的时候就打开一个线程去监听客户端请求
public class ServerServlet implements Servlet {
public void init(ServletConfig arg0) throws ServletException {
Handler handler = new Handler();
Thread thread = new Thread(handler);
thread.start();
}
//客户端连接线程池
private ExecutorService executor = Executors.newFixedThreadPool(20);
//客户端请求处理线程池,此处要注意客户请求是否有先后顺序、共享资源
private ExecutorService bizExecutor = Executors.newFixedThreadPool(1);
//创建一个阻塞队列保存客户的请求信息
public LinkedBlockingQueue<ClientInfo> queue = new LinkedBlockingQueue<ClientInfo>();
//保存客户请求信息实体
class ClientInfo{
private String xml;
private String ip;
public ClientInfo(String xml, String ip){
super();
this.xml = xml;
this.ip = ip;
}
public String getXml() {
return xml;
}
public void setXml(String xml) {
this.xml = xml;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
}
//服务器线程,监听客户端请求
class Handler implements Runnable{
public void run(){
//开启一个业务处理线程,处理客户端的请求
bizExecutor.submit(new BizHandler());
ServerSocket server = null;
try{
server = new ServerSocket(11480);
} catch(Exception e){
e.printStackTrace();
}
if(server != null){
System.out.println("服务器端口已打开");
while(true){
try{
Socket socket = server.accept();
//开启一个与客户端连接的线程
ProgressThread proThread = new ProgressThread(socket);
executor.submit(proThread);
} catch (Exception e){
e.printStackTrace();
}
}
}
}
}
//客户端连接线程
class ProgressThread implements Runnable{
private Socket socket;
public ProgressThread(Socket socket){
super();
this.socket = socket;
}
public void run(){
try{
StringBuilder sb = new StringBuilder();
InputStream is = socket.getInputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is,"utf-8"));
String line = null;
//以行为单位读取数据,长连接时若无换行符将不能读出数据,只能在socket关闭时一次性得到数据
while((line = in.readLine()) != null){
if(line.trim().endsWith("/xml>")){
sb.append(line);
if(sb != null && !"".equals(sb.toString().trim())){
queue.put(new ClientInfo(sb.toString(),this.socket.getLocalAddress().toString()));
}
sb = new StringBuilder();
} else {
sb.append(line);
}
}
// //没有换行符的读取方式,需捕获connect reset异常
// while (true){
// try{
// char[] ch = new char[1024];
// int n = in.read(ch);
// System.out.println(new String(ch, 0, n));
// } catch(Exception e){
// System.out.println("客户端连接已关闭");
// break;
// }
// }
}catch(Exception e){
e.printStackTrace();
}
}
}
//客户请求信息处理线程
class BizHandler implements Runnable{
public void run(){
while(true){
try{
//取出阻塞队列中的信息处理
final ClientInfo info = queue.poll();
if(info == null){
Thread.sleep(5000);
} else {
writeData(info.getXml(),info.getIp());
}
} catch(Exception e){
e.printStackTrace();
}
}
}
public void writeData(String str1, String str2){
System.out.println(str1);
System.out.println(str2);
}
}
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException {
}
}
2. 测试客户端
package test;
import java.io.OutputStream;
import java.net.Socket;
public class ClientTest {
public static void main(String[] args) {
Socket socket;
try {
socket = new Socket("localhost", 11480);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("<xml>Hello</xml>\r\n".getBytes("utf-8"));
outputStream.flush();
System.out.println(socket);
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}