一、TCP实现小型聊天室
实现方法:服务器端接受所有的消息,然后把接收到的每一条消息发送给所有已连接的设备上去。
服务器端代码:
1 package com.TCP; 2 3 import java.util.*; 4 5 6 import java.net.*; 7 import java.io.*; 8 public class TCPServer_2 { 9 10 private int no = 0; //为连接设备编号 11 12 // 保存当前连接的客户端Socket 13 private List<Socket> sockList = Collections.synchronizedList(new ArrayList<Socket>()); 14 15 class ServerThread implements Runnable { 16 int no; 17 Socket s; 18 BufferedReader br; 19 20 public ServerThread(Socket s, int no) { 21 this.s = s; 22 this.no = no; 23 try { 24 br = new BufferedReader(new InputStreamReader(s.getInputStream())); 25 } catch (IOException e) { 26 e.printStackTrace(); 27 } 28 } 29 30 private String getContentByLine() { 31 try { 32 return br.readLine(); 33 } catch (IOException e) { // 该请求可能已被客户端关闭 34 e.printStackTrace(); 35 sockList.remove(s); // 因此需要从列表中移除 36 } 37 return null; 38 } 39 40 @Override 41 public void run() { 42 String content = ""; 43 while ((content = getContentByLine()) != null) { 44 // 只要客户端不关闭Socket,那么输入输出流就是一直开着的 45 // 服务器端会一直等待输入,永远也到不了EOF使getContentByLine返回null 46 for (Socket s: sockList) { // 对每一个连接的客户端都发送聊天信息 47 PrintStream ps; 48 try { 49 // ps = new PrintStream(s.getOutputStream()); 50 // ps.println("#" + no + " says: " + content); 51 // ps.println(x); 52 DataOutputStream dataOut = new DataOutputStream(s.getOutputStream()); 53 if("CI".equals(content.substring(0, 2))){ 54 dataOut.writeBytes("SCXX" + ' '); 55 } 56 else if("CS".equals(content.substring(0, 2))){ 57 dataOut.writeBytes("SLXX" + ' '); 58 }else{ 59 dataOut.writeBytes("#" + no + " says: " + content); 60 } 61 } catch (IOException e) { 62 // TODO Auto-generated catch block 63 e.printStackTrace(); 64 } 65 } 66 } 67 } 68 69 } 70 71 public void init() throws IOException { 72 // ServerSocket ss = new ServerSocket(8000, 10, InetAddress.getByName("192.168.96.1")); // 使用默认IP,端口3000 73 ServerSocket ss = new ServerSocket(8000); 74 while (true) { 75 Socket s = ss.accept(); 76 no += 1; 77 System.out.println("#" + no + " connected!"); 78 sockList.add(s); 79 new Thread(new ServerThread(s, no)).start(); 80 } 81 } 82 83 public static void main(String[] args) throws IOException { 84 new TCPServer_2().init(); 85 } 86 87 }
客户端代码:
1 package com.TCP; 2 3 import java.util.*; 4 import java.net.*; 5 import java.io.*; 6 public class TCPClient_2 { 7 8 9 class ClientThread implements Runnable { 10 private Socket s; 11 12 public ClientThread(Socket s) { 13 this.s = s; 14 } 15 16 @Override 17 public void run() { 18 try { 19 String content = ""; 20 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); 21 while ((content = br.readLine()) != null) { // 不断读取服务器发来的广播消息 22 System.out.println(content); 23 } 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } 27 } 28 } 29 30 public void init() throws UnknownHostException, IOException { 31 Socket s = new Socket("192.168.1.106", 8000); 32 new Thread(new ClientThread(s)).start(); // 建立连接后马上监控广播消息 33 34 PrintStream ps = new PrintStream(s.getOutputStream()); // 输出流定向到远程 35 36 String line = ""; 37 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 38 while ((line = br.readLine()) != null) { // 从控制台键盘读取输入 39 ps.println(line); // 发送到远程 40 } 41 } 42 43 public static void main(String[] args) throws UnknownHostException, IOException { 44 new TCPClient_2().init(); 45 } 46 47 }
二、UDP实现组播播消息(UDP没有c/s概念,所以多开几个程序就能相互通信了)
1 package com.UDP_Port; 2 3 import java.io.IOException; 4 import java.net.DatagramPacket; 5 import java.net.InetAddress; 6 import java.net.MulticastSocket; 7 import java.util.Scanner; 8 9 public class UDPMulticast { 10 11 private final static String BC_IP = "230.0.0.1"; // 组播地址 12 private final static int BC_PORT = 30000; // 组播端口 13 private final static int PACK_SIZE = 4096; 14 15 public void init() throws IOException { 16 MulticastSocket sock = new MulticastSocket(BC_PORT); 17 InetAddress bcAddr = InetAddress.getByName(BC_IP); 18 try (Scanner scan = new Scanner(System.in)) { 19 // 创建socket并加入组播地址 20 sock.joinGroup(bcAddr); 21 sock.setLoopbackMode(false); // 必须是false才能开启广播功能!! 22 23 new Thread(() -> { // 接受广播消息的线程 24 try { 25 DatagramPacket inpack = new DatagramPacket(new byte[PACK_SIZE], PACK_SIZE); 26 while (true) { 27 sock.receive(inpack); 28 System.out.println("广播消息:" + new String(inpack.getData(), 0, inpack.getLength())); 29 } 30 } catch (IOException e) { 31 e.printStackTrace(); 32 if (sock != null) { 33 try { 34 sock.leaveGroup(bcAddr); 35 } catch (Exception e1) { 36 // TODO Auto-generated catch block 37 e1.printStackTrace(); 38 } 39 sock.close(); 40 } 41 System.exit(1); 42 } 43 }).start(); 44 45 // 主线程接受控制台输入并广播出去 46 DatagramPacket outpack = new DatagramPacket(new byte[0], 0, bcAddr, BC_PORT); // 目的端口和MulticastSocket端口一样!! 47 while (scan.hasNextLine()) { 48 byte[] buf = scan.nextLine().getBytes(); 49 outpack.setData(buf); 50 sock.send(outpack); 51 } 52 } finally { // 最终关闭程序之前一定要关闭socket 53 sock.close(); 54 } 55 } 56 57 public static void main(String[] args) throws NumberFormatException, IOException { 58 // TODO Auto-generated method stub 59 new UDPMulticast().init(); 60 } 61 62 }