转自 http://blog.csdn.net/ooppookid/article/details/51711323
1、如何编写多线程Socket程序
了解Socket看这里:Socket是什么
多线程Socket与单线程类似,只是使用了多线程的方式来管理连接,主线程负责接收连接,在接到连接后变创建新的线程,每个线程负责与自己的客户端进行通信。
了解单线程Socket看这里:如何编写单多线程Socket程序
与单线程Socket例子相比来说,服务端可以与多个客户端进行通信了,不过多线程频繁的创建与销毁便会带来很大的资源开销,而系统的网络资源等都是有限的。因此一般会引入线程池,可以在某种程度上重用线程,减少线程的创建和销毁的次数以减少开销。
我们的代码也分为客户端和服务端两部分。服务端的代码中包含了使用和不使用线程池的两种方式。
服务端代码:
1 import java.io.BufferedReader;
2 import java.io.IOException;
3 import java.io.InputStreamReader;
4 import java.io.PrintWriter;
5 import java.net.ServerSocket;
6 import java.net.Socket;
7 import java.util.concurrent.ExecutorService;
8 import java.util.concurrent.Executors;
9
10 public class SocketThreadPoolDemoServer {
11
12 private int port = 8000;
13
14 private ServerSocket serverSocket;
15
16 private ExecutorService executorService; // 连接池
17
18 private final int POOL_SIZE = 1; // 连接池大小 , 若为 1 时最多支持 2 线程
19
20 public SocketThreadPoolDemoServer() throws Exception {
21 serverSocket = new ServerSocket(port);
22 executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL_SIZE);// 初始化线程池
23 System.out.println("waitting connet");
24
25 }
26
27 /**
28 *
29 * 接受连接
30 *
31 * @author sunjie at 2016年6月14日
32 *
33 */
34 public void service() {
35 Socket socket = null;
36 while (true) {
37 try {
38 socket = serverSocket.accept();
39 executorService.execute(new Handler(socket)); // 使用连接池
40 // new Thread(new Handler(socket)).start();// 不使用连接池
41 } catch (IOException e) {
42 e.printStackTrace();
43 }
44 }
45 }
46
47 /**
48 *
49 * 线程类,负责维持与一个客户端的通信
50 *
51 * @author sunjie at 2016年6月14日
52 *
53 */
54 class Handler implements Runnable {
55
56 private Socket socket = null;
57
58 public Handler(Socket socket) {
59 this.socket = socket;
60 }
61
62 @Override
63 public void run() {
64 System.out.println("new connection accepted:" + socket.getInetAddress() + ":" + socket.getPort());
65 try {
66 BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
67 PrintWriter writer = new PrintWriter(socket.getOutputStream());
68 String msg = null;
69 while ((msg = reader.readLine()) != null) {
70 System.out.println("from " + socket.getInetAddress() + ":" + socket.getPort() + ", receive msg:"
71 + msg);
72 writer.println(msg);
73 writer.flush();
74 if ("close".equals(msg)) {
75 break;
76 }
77 }
78 } catch (IOException e) {
79 e.printStackTrace();
80 } finally {
81 try {
82 if (socket != null) {
83 socket.close();
84 }
85 } catch (IOException e) {
86 e.printStackTrace();
87 }
88 }
89 }
90 }
91
92 public static void main(String[] args) throws Exception {
93 new SocketThreadPoolDemoServer().service();
94 }
95 }
96
97 运行服务端代码后,程序会一直进行监听,直到接收到客户端请求为止。结果如下:
waitting connet…
客户端代码(与单线程完全相同):
1 public class SocketDemoClient {
2
3 private String host = "127.0.0.1";// 要发送给服务端的ip
4
5 private int port = 8000;// 要发送给服务端的端口
6
7 private Socket socket;
8
9 public SocketDemoClient() throws Exception {
10 socket = new Socket(host, port);// 构造Socket客户端,并与连接服务端
11 }
12
13 public void talk() throws IOException {
14 try {
15 BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
16 PrintWriter writer = new PrintWriter(socket.getOutputStream());
17 // 读取本地控制台的消息
18 BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
19 String msg = null;
20 while ((msg = localReader.readLine()) != null) {
21 writer.println(msg);
22 writer.flush();
23 System.out.println("send msg:" + reader.readLine());
24 if ("close".equals(msg)) {
25 break;
26 }
27 }
28 } catch (Exception e) {
29 e.printStackTrace();
30 } finally {
31 if (socket != null) {
32 socket.close();
33 }
34 }
35 }
36
37 public static void main(String[] args) throws Exception {
38 new SocketDemoClient().talk();
39 }
40 }
由于我们要测试多个客户端连接同一个服务端,所以我们需要多次运行客户端代码。这里我们运行两次之后(称为客户端1、客户端2),查看服务端的Console,会出现以下结果,说明已经连接成功:
waitting connet…
new connection accepted:/127.0.0.1:59593
new connection accepted:/127.0.0.1:59596
我们在去客户端1的Console中输入我们要发送的消息”维护世界和平”,回车确定后,客户端1的Console出现以下结果,消息已经发出:
send msg:维护世界和平
再去客户端2的Console中输入”好好学习天天向上”,回车确定后,客户端2的Console出现以下结果,消息已经发出:
send msg:好好学习天天向上
在服务端的Console中,我们会看到如下结果,说明两个客户端的消息已经被接受:
waitting connet…
new connection accepted:/127.0.0.1:59593
new connection accepted:/127.0.0.1:59596
from /127.0.0.1:59593, receive msg:维护世界和平
from /127.0.0.1:59596, receive msg:好好学习天天向上