前面一片学习了TCP/IP的基础网络编程,并给出了简单的服务端与客户端通信交互的例子。还介绍了UPC的通信例子。
这次学习TCP/IP的多线程编程。因为涉及到TCP/IP一般都是多线程,服务端会一直监听端口,多个客户端发来信息,收到某个客户端发来的数据后,如果所有处理都放在服务端,这样程序就会显得很臃肿。就需要单独启动一个线程去执行,来一个客户端就启动一个对应的程序。
下面给出具体例子:Java 多线程实现Socket通信
1 package lesson1220Socket; 2 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 6 public class SeverTCPDemo { 7 8 private static ServerSocket ss; //定义成static类型是有原因的,因为ss不能进行close. 9 10 public static void main(String[] args) throws Exception { 11 ss = new ServerSocket(9999); 12 Socket socket = null; 13 int num = 0; 14 System.out.println("******服务器已启动,等待客户端连接*****"); 15 16 while(true){ 17 socket = ss.accept(); 18 num++; 19 new SocketThread(socket,num).start(); 20 } 21 } 22 } 23 24 package lesson1220Socket; 25 26 import java.io.BufferedReader; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.InputStreamReader; 30 import java.io.OutputStream; 31 import java.io.PrintWriter; 32 import java.net.Socket; 33 34 public class SocketThread extends Thread { 35 36 Socket socket; 37 private int num; 38 39 public SocketThread(Socket socket, int num) { 40 super(); 41 this.socket = socket; 42 this.num = num; 43 } 44 45 /* (non-Javadoc) 46 * @see java.lang.Thread#run() 47 */ 48 @Override 49 public void run() { 50 51 InputStream is = null; 52 BufferedReader br = null; 53 OutputStream os = null; 54 PrintWriter pw = null; 55 try { 56 is = socket.getInputStream(); 57 br = new BufferedReader(new InputStreamReader(is)); 58 String info = null; 59 while((info=br.readLine())!=null){ 60 System.out.println("客户端发来消息:" + info); 61 } 62 socket.shutdownInput(); 63 64 os = socket.getOutputStream(); 65 pw= new PrintWriter(os); 66 pw.write("hello, I am Server! you are " + num + " Client!"); 67 pw.flush(); 68 socket.shutdownOutput(); 69 70 } catch (IOException e) { 71 e.printStackTrace(); 72 }finally{ 73 try { 74 is.close(); 75 } catch (IOException e) { 76 e.printStackTrace(); 77 } 78 try { 79 br.close(); 80 } catch (IOException e) { 81 e.printStackTrace(); 82 } 83 try { 84 os.close(); 85 } catch (IOException e) { 86 e.printStackTrace(); 87 } 88 pw.close(); 89 } 90 } 91 } 92 93 package lesson1220Socket; 94 95 import java.io.BufferedReader; 96 import java.io.IOException; 97 import java.io.InputStream; 98 import java.io.InputStreamReader; 99 import java.io.OutputStream; 100 import java.io.PrintWriter; 101 import java.net.Socket; 102 import java.net.UnknownHostException; 103 104 public class ClientTCPDemo { 105 106 public static void main(String[] args){ 107 108 Socket socket = null; 109 OutputStream os = null; 110 PrintWriter pw = null; 111 InputStream is = null; 112 BufferedReader br = null; 113 114 try { 115 socket = new Socket("127.0.0.1", 9999); 116 os = socket.getOutputStream(); 117 pw = new PrintWriter(os); 118 pw.write("hello, server! I am client!"); 119 pw.flush(); 120 121 socket.shutdownOutput(); 122 123 is = socket.getInputStream(); 124 br = new BufferedReader(new InputStreamReader(is)); 125 String info = null; 126 while((info=br.readLine())!=null){ 127 System.out.println("服务端返回信息:" + info); 128 } 129 130 socket.shutdownInput(); 131 132 } catch (UnknownHostException e) { 133 e.printStackTrace(); 134 } catch (IOException e) { 135 e.printStackTrace(); 136 }finally{ 137 try { 138 os.close(); 139 } catch (IOException e1) { 140 e1.printStackTrace(); 141 } 142 pw.close(); 143 try { 144 is.close(); 145 } catch (IOException e) { 146 e.printStackTrace(); 147 } 148 try { 149 br.close(); 150 } catch (IOException e) { 151 e.printStackTrace(); 152 } 153 try { 154 socket.close(); 155 } catch (IOException e) { 156 e.printStackTrace(); 157 } 158 } 159 } 160 161 }
运行结果:分别启动三个客户端:
******服务器已启动,等待客户端连接*****
客户端发来消息:hello, server! I am client!
客户端发来消息:hello, server! I am client!
客户端发来消息:hello, server! I am client!
客户端:
服务端返回信息:hello, I am Server! you are 1 Client!
服务端返回信息:hello, I am Server! you are 2 Client!
服务端返回信息:hello, I am Server! you are 3 Client!
Note:一定要等程序都运行完后,才可以关闭相关资源例如:os,is,pw等。
上面的例子通信一次就关闭了,下面再给出几个TCP/IP通信例子,两边可以交互通信:
例子1:两边通信,按顺序一发一收。客户端和服务器都有收发功能,并且收发有相应的顺序,都对应相应的阻塞:缓冲区br.readLine(), 键盘输入sc.nextLine()。
1 package lesson1220Socket; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 import java.io.OutputStream; 8 import java.io.OutputStreamWriter; 9 import java.io.PrintWriter; 10 import java.net.ServerSocket; 11 import java.net.Socket; 12 import java.util.Scanner; 13 14 public class ChatServer { 15 public static ServerSocket ss; 16 17 public ChatServer() { 18 try { 19 ss = new ServerSocket(8888); 20 } catch (IOException e) { 21 e.printStackTrace(); 22 } 23 } 24 25 public void start(){ 26 Socket socket = null; 27 InputStream is = null; 28 InputStreamReader isr = null; 29 BufferedReader br = null; 30 OutputStream os = null; 31 PrintWriter pw = null; 32 33 try { 34 System.out.println("服务端启动。。。。。。"); 35 socket = ss.accept(); 36 37 is = socket.getInputStream(); 38 isr = new InputStreamReader(is); 39 br = new BufferedReader(isr); 40 41 os = socket.getOutputStream(); 42 //pw = new PrintWriter(os); //很奇怪,这个地方要这么写,要不然数据发不出去? 43 OutputStreamWriter osw = new OutputStreamWriter(os); 44 pw = new PrintWriter(osw, true);//解释上一行,要输入参数true,会自动刷新flush() 45 46 Scanner sc = new Scanner(System.in); 47 //也可以通过下面code来获取系统输入 48 //BufferedReader br2 = new BufferedReader(new InputStreamReader(System.in)); 49 //br2.readLine(); 50 51 String info; 52 53 while(true){ 54 55 System.out.println("收到客户端消息: " + br.readLine()); 56 pw.println(info = sc.nextLine()); 57 //pw.println(info = br2.readLine()); 58 if("bye".equals(info)){ 59 System.out.println("byebye!"); 60 break; 61 } 62 } 63 64 } catch (IOException e) { 65 e.printStackTrace(); 66 } 67 } 68 69 public static void main(String[] args) { 70 new ChatServer().start(); 71 } 72 } 73 74 package lesson1220Socket; 75 76 import java.io.BufferedReader; 77 import java.io.IOException; 78 import java.io.InputStream; 79 import java.io.InputStreamReader; 80 import java.io.OutputStream; 81 import java.io.PrintWriter; 82 import java.net.Socket; 83 import java.util.Scanner; 84 85 public class ChatClient { 86 87 Socket socket ; 88 89 public ChatClient() { 90 try { 91 this.socket = new Socket("127.0.0.1", 8888); 92 } catch (IOException e) { 93 e.printStackTrace(); 94 } 95 } 96 97 public void start(){ 98 99 InputStream is = null; 100 InputStreamReader isr = null; 101 BufferedReader br = null; 102 OutputStream os = null; 103 PrintWriter pw = null; 104 try { 105 is = socket.getInputStream(); 106 isr = new InputStreamReader(is); 107 br = new BufferedReader(isr); 108 os = socket.getOutputStream(); 109 pw = new PrintWriter(os,true); //true代表 每写一行自动刷新 110 111 Scanner sc = new Scanner(System.in); 112 //BufferedReader br2 = new BufferedReader(new InputStreamReader(System.in)); 113 //br2.readLine(); 114 115 String info; 116 while(true){ 117 pw.println(info = sc.nextLine()); 118 //pw.write(info = sc.nextLine()+" "); 119 //pw.flush(); 120 //详细讲解可以看帖子:https://www.cnblogs.com/wxgblogs/p/5347996.html 121 System.out.println("收到服务端回应消息:" + br.readLine()); 122 123 if("bye".equals(info)){ 124 System.out.println("byebye!"); 125 break; 126 } 127 } 128 129 } catch (IOException e) { 130 e.printStackTrace(); 131 } 132 133 } 134 135 public static void main(String[] args) { 136 new ChatClient().start(); 137 } 138 139 }
必须按顺序进行收发,不能同时进行。
要想收发同时进行,收发就不能再同一个线程内,就应该有两个线程,一个负责发,一个负责收。
例子2:通过将收发单独分开,用线程实现。两边可以任意发送。
1 package lesson1220Socket; 2 3 import java.io.IOException; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 public class ChatServer2 { 8 9 10 public static ServerSocket ss; 11 private Socket socket; 12 13 public ChatServer2() { 14 try { 15 ss = new ServerSocket(8888); 16 socket = ss.accept(); 17 } catch (IOException e) { 18 e.printStackTrace(); 19 } 20 } 21 22 public void start(){ 23 24 Thread st = new Thread(new SendThread(socket)); 25 st.start(); 26 Thread rv = new Thread(new ReceiveThread(socket)); 27 rv.start(); 28 29 } 30 31 public static void main(String[] args) { 32 new ChatServer2().start(); 33 } 34 35 } 36 37 package lesson1220Socket; 38 39 import java.io.IOException; 40 import java.net.Socket; 41 42 43 public class ChatClient2 { 44 Socket socket ; 45 46 public ChatClient2() { 47 try { 48 this.socket = new Socket("127.0.0.1", 8888); 49 } catch (IOException e) { 50 e.printStackTrace(); 51 } 52 } 53 54 public void start(){ 55 56 Thread st = new Thread(new SendThread(socket)); 57 st.start(); 58 Thread rv = new Thread(new ReceiveThread(socket)); 59 rv.start(); 60 61 } 62 63 public static void main(String[] args) { 64 new ChatClient2().start(); 65 } 66 67 } 68 69 package lesson1220Socket; 70 71 import java.io.IOException; 72 import java.io.OutputStream; 73 import java.io.PrintWriter; 74 import java.net.Socket; 75 import java.util.Scanner; 76 77 public class SendThread implements Runnable { 78 79 private Socket socket; 80 public SendThread(Socket socket) { 81 this.socket = socket; 82 } 83 84 @Override 85 public void run() { 86 87 OutputStream os = null; 88 PrintWriter pw = null; 89 90 try { 91 os = socket.getOutputStream(); 92 pw = new PrintWriter(os, true); 93 Scanner sc = new Scanner(System.in); 94 95 while(true){ 96 pw.println(sc.nextLine()); 97 } 98 } catch (IOException e) { 99 e.printStackTrace(); 100 } 101 } 102 } 103 104 package lesson1220Socket; 105 106 import java.io.BufferedReader; 107 import java.io.IOException; 108 import java.io.InputStream; 109 import java.io.InputStreamReader; 110 import java.net.Socket; 111 112 public class ReceiveThread implements Runnable { 113 114 Socket socket; 115 public ReceiveThread(Socket socket) { 116 this.socket = socket; 117 } 118 119 @Override 120 public void run() { 121 122 InputStream is = null; 123 InputStreamReader isr = null; 124 BufferedReader br = null; 125 126 try { 127 is = socket.getInputStream(); 128 isr = new InputStreamReader(is); 129 br = new BufferedReader(isr); 130 131 while(true){ 132 System.out.println("收到消息:" + br.readLine()); 133 } 134 135 } catch (IOException e) { 136 e.printStackTrace(); 137 } 138 } 139 }
服务端和客户端共用收发线程,只要两边Socket连接建立完成,所有的工作都是在收发线程完成。
下面考虑多发通信问题。
例子3:一对多通信。 多个客户端同时给服务端发消息,然后服务端回消息,这个回的是群发消息。
1 package lesson1220Socket; 2 3 import java.io.IOException; 4 import java.io.OutputStream; 5 import java.io.PrintWriter; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 import java.util.ArrayList; 9 import java.util.Scanner; 10 11 public class ChatServer3 { 12 13 public static ServerSocket ss; 14 Socket cs; 15 ArrayList<Socket> list = new ArrayList<Socket>(); 16 private boolean isGroup = true; 17 private Scanner input = new Scanner(System.in); 18 19 public ChatServer3() { 20 21 try { 22 ss = new ServerSocket(7777); 23 } catch (IOException e) { 24 e.printStackTrace(); 25 } 26 } 27 28 public void start(){ 29 System.out.println("服务端启动。。。"); 30 while(true){ 31 try { 32 cs = ss.accept(); 33 list.add(cs); 34 System.out.println("新的连接" + cs.getPort()); 35 36 Thread receiveThread = new Thread(new ReceiveThread(cs)); 37 receiveThread.start(); 38 39 if(isGroup){ 40 isGroup = false; 41 new SendFunc().start(); 42 } 43 44 } catch (IOException e) { 45 e.printStackTrace(); 46 } 47 } 48 } 49 50 public class SendFunc extends Thread { 51 @Override 52 public void run() { 53 OutputStream os = null; 54 PrintWriter pw = null; 55 while(true){ 56 try { 57 String msg =input.nextLine(); 58 for(Socket sc:list){ 59 os = sc.getOutputStream(); 60 pw = new PrintWriter(os, true); 61 pw.println(msg); 62 } 63 } catch (IOException e) { 64 e.printStackTrace(); 65 } 66 } 67 } 68 } 69 70 public static void main(String[] args) { 71 new ChatServer3().start(); 72 } 73 74 } 75 76 package lesson1220Socket; 77 78 import java.io.IOException; 79 import java.net.Socket; 80 81 public class ChatClient3 { 82 Socket socket; 83 84 public ChatClient3() { 85 try { 86 System.out.println("客户端启动。。。。。"); 87 socket = new Socket("127.0.0.1", 7777); 88 } catch (IOException e) { 89 e.printStackTrace(); 90 } 91 } 92 93 public void start(){ 94 Thread sendThread = new Thread(new SendThread(socket)); 95 sendThread.start(); 96 97 Thread receiveThread = new Thread(new ReceiveThread(socket)); 98 receiveThread.start(); 99 } 100 101 public static void main(String[] args) { 102 new ChatClient3().start(); 103 } 104 105 } 106 107 package lesson1220Socket; 108 109 import java.io.IOException; 110 import java.io.OutputStream; 111 import java.io.PrintWriter; 112 import java.net.Socket; 113 import java.util.Scanner; 114 115 public class SendThread implements Runnable { 116 117 private Socket socket; 118 public SendThread(Socket socket) { 119 this.socket = socket; 120 } 121 122 @Override 123 public void run() { 124 125 OutputStream os = null; 126 PrintWriter pw = null; 127 128 try { 129 os = socket.getOutputStream(); 130 pw = new PrintWriter(os, true); 131 Scanner sc = new Scanner(System.in); 132 133 while(true){ 134 pw.println(sc.nextLine()); 135 } 136 } catch (IOException e) { 137 e.printStackTrace(); 138 } 139 } 140 } 141 142 package lesson1220Socket; 143 144 import java.io.BufferedReader; 145 import java.io.IOException; 146 import java.io.InputStream; 147 import java.io.InputStreamReader; 148 import java.net.Socket; 149 150 public class ReceiveThread implements Runnable { 151 152 Socket socket; 153 public ReceiveThread(Socket socket) { 154 this.socket = socket; 155 } 156 157 @Override 158 public void run() { 159 160 InputStream is = null; 161 InputStreamReader isr = null; 162 BufferedReader br = null; 163 164 try { 165 is = socket.getInputStream(); 166 isr = new InputStreamReader(is); 167 br = new BufferedReader(isr); 168 169 while(true){ 170 System.out.println("收到消息:" + br.readLine()); 171 } 172 173 } catch (IOException e) { 174 e.printStackTrace(); 175 } 176 } 177 }
上面code可以实现多个客户端向服务端发送消息,服务端可以群发消息。客户端没有做任何改变。
只需修改服务端,就是如何让消息群发出去,由一个线程来控制,循环遍历所有socket,然后将消息发出去。
网上很多帖子都是服务端自动回一个固定消息,实际上没有实现一对多通信。
但是上面的功能不能实现服务端向指定客户端回消息。若是收到一个客户端,服务端就启动一个收发线程,收的线程没有问题,可是发的线程有问题。 没法进行维护?也不知道该数据发向哪个Socket????
下面这两个也是很好的通过线程来调用Socket的例子:
https://www.cnblogs.com/sddychj/p/6102192.html
https://www.cnblogs.com/qqzy168/p/3772215.html
https://blog.csdn.net/sl_40/article/details/53232423------这个博客解决了我群发消息的问题。
https://blog.csdn.net/aiynmimi/article/details/47323165------这个例子也实现多个客户端向服务端发消息,服务端群发消息。这个里面有界面,通过ActionListener来实现群发!