《互联网程序设计(Java)》——课程笔记6:多用户服务器程序设计
程序员文章站
2024-03-19 13:44:04
...
学会服务器支持多用户并发访问的程序设计技术。
1.多用户服务器是指服务器能同时支持多个用户并发访问服务器所提供的服务资源,如聊天服务、文件传输等。
2.TCPServer.java
服务器程序是单用户版本,每次只能和一个用户对话。(请仔细阅读TCPServer.java程序,了解其中原理,找出关键语句),只有前一个用户退出后,后面的用户才能完成服务器连接。
演示过程:
(1) 启动你的TCPServer.java程序(程序详见之前附录代码);
(2) 启动你的TCPClientThreadJFrame.java程序,完成一次对话,并保持在线;
(3) 再启动你的TCPClientThreadJFrame.java程序2次,并完成连接,尝试对话,发现无返回信息(服务器和客户端各阻塞在哪条语句?);
(4) 退出第一次启动的TCPClientThreadJFrame.java程序,发现第二次启动的客户端有返回信息了,说明在一个客户退出后,第二个客户才连接成功。
原因:服务器的主进程一次只能处理一个客户,但所有已连接的客户等候在监听队列中。
3.多用户服务器程序设计
TCPServer.java程序能同时支持多个用户并发连接(TCP三次握手),但不能同时服务多用户对话。
程序设计第一步:解决服务多用户对话问题。
用一个线程专门负责和一个客户对话,即一个客户请求成功后,创建一个线程来专门负责该客户,主进程只负责监听客户请求和接受连接请求。
public class TCPThreadServer {
private int port = 8008;
private ServerSocket serverSocket;
private ExecutorService executorService; //线程池
private final int POOL_SIZE = 100; //单个CPU时线程池中工作线程的数目
public TCPThreadServer() throws IOException
{
serverSocket = new ServerSocket(port); //打开服务器端口
//创建线程池,Runtime的Availableprocessor()方法返回当前系统的CPU的数目
//系统中CPU越多,线程池中工作线程的数目也应该越多
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*POOL_SIZE);
System.out.println("多用户服务器启动");
}
public void service()
{
while(true)
{
Socket socket = null;
try
{
socket = serverSocket.accept(); //监听客户请求,阻塞语句
//将socket放入Members列表中
//接受一个客户请求,从线程池中拿出一个线程专门处理该客户
executorService.execute(new Handler(socket));
}catch(Exception e)
{
e.printStackTrace();
}
}
}
public static void main(String args[]) throws IOException
{
new TCPThreadServer().service();
}
}
//定义内部类,实现线程接口
class Handler implements Runnable
{
private Socket socket;
public Handler(Socket socket)
{
this.socket = socket;
}
private PrintWriter getWriter(Socket socket) throws IOException
{
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(new OutputStreamWriter(socketOut,"GB2312"),true);
}
private BufferedReader getReader(Socket socket) throws IOException
{
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn,"GB2312"));
}
重写Runnable:
@Override
public void run() {
try
{
System.out.println("New connection accepted " + socket.getInetAddress().getHostAddress() + ":"+socket.getPort());
BufferedReader br = getReader(socket);//字节流封装为字符流
PrintWriter pw = getWriter(socket); //字节流封装为字符流
String msg = null;
while((msg = br.readLine())!= null)
{
pw.println("From ThreadServer: " + msg); //send to client
if(msg.contains("bye".substring(0, 2)))
{
System.out.println(socket.getInetAddress() + " : " + "Exit");
break;
}
}
}catch(Exception e)
{
e.printStackTrace();
}finally
{
try
{
if(socket!=null)
{
socket.close();
}
}catch(IOException e)
{
e.printStackTrace();
}
}
}