本文共 6111 字,大约阅读时间需要 20 分钟。
【JavaSE】day15_TCP之聊天室
package day07chat;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List;/** * 聊天室服务端 * java.net.ServerSocket * ServerSocket是运行在服务端的,其作用是向系统 * 申请服务端口,以便监听该端口,等待客户端的连接. * 一旦一个客户端连接,就会创建一个Socket与 * 该客户端进行通信. */public class Server { //运行在服务端的ServerSocket private ServerSocket server; //存放所有输出流的集合,用于广播消息 private ListallOut; /** * 构造方法,用来初始化服务端 */ public Server(){ try{ allOut = new ArrayList (); /* * 初始化ServerSocket的同时需要指定服务端口,该端口不能与当前系统 * 使用TCP协议的其他程序申请的端口冲突,否则会抛出端口被占用的异常. */ server = new ServerSocket(8088); }catch(Exception e){ e.printStackTrace(); } } private synchronized void addOut(PrintWriter pw){ allOut.add(pw); } private synchronized void removeOut(PrintWriter pw){ allOut.remove(pw); } private synchronized void sendMessageToAllClient(String m){ for(PrintWriter pw : allOut){ pw.println(m); } } /** * 服务端开始工作的方法 */ public void start(){ try{ /* * Socket accept() * ServerSocket提供的该方法用来监听打开的服务端口(8088), * 该方法是一个阻塞方法,直到一个客户端尝试连接才会解除阻塞, * 并创建一个Socket与刚才连接的客户端进行通讯. * * accept方法每次调用都会等待一个客户端连接,所以若希望服务端 * 能够接受若干客户端的连接,就需要多次调用该方法,来分别获取对应 * 这些客户端的Socket与他们通讯. * * 查看本机ip: * windows:ipconfig * linux:/sbin/ifconfig */ while(true){ System.out.println("等待客户端连接..."); Socket socket = server.accept(); System.out.println("一个客户端连接了!"); /* * 当一个客户端连接后,启动一个线程,来负责与该客户端交互. */ ClientHandler handler = new ClientHandler(socket); Thread t = new Thread(handler); t.start(); } }catch(Exception e){ e.printStackTrace(); } } public static void main(String[] args){ Server server = new Server(); server.start(); } /** * 该线程用来与一个指定的客户端进行交互. * 每当一个客户端连接服务端后,都会启动当前线程来负责与之交互工作. * */ private class ClientHandler implements Runnable{ //当前线程交互的客户端的Socket private Socket socket; //客户端的地址信息 private String host; public ClientHandler(Socket socket){ this.socket = socket; //通过socket可以得到远端计算机信息 InetAddress address = socket.getInetAddress(); //获取远端计算机IP host = address.getHostAddress(); } public void run() { PrintWriter pw = null; try{ /* * InputStream getInputStream() * Socket提供的该方法用来获取输入流,读取远端计算机 * 发送过来的数据. */ InputStream in = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(in,"UTF-8"); BufferedReader br = new BufferedReader(isr); /* * 通过客户端的Socket获取输出流,以便将消息 * 发送给客户端. */ OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8"); pw = new PrintWriter(osw,true); //共享该客户端的输出流 addOut(pw); //广播该用户上线. sendMessageToAllClient(host+"上线了"); /* * 当我们使用BufferedReader读取来自远端计算机发送过来的内容时, * 由于远端计算机的操作系统不同,当它们断开连接时,这里readLine * 方法的结果也不同: * windows->抛异常 * linux->返回null */ //Scanner scan = new Scanner(System.in); String str = null; while((str=br.readLine())!=null){ sendMessageToAllClient(host+"说:"+str); } }catch(Exception e){ e.printStackTrace(); }finally{ /* * 当该客户端与服务器断开时,应当将该客户端从输出流 * 共享集合中删除. */ removeOut(pw); //广播该用户下线. sendMessageToAllClient(host+"下线了"); /* * 无论时linux的客户端,还是windows的客户端,当与 * 服务端断开连接后,都应当将与该客户端交互的Socket * 关闭,来释放底层资源. */ if(socket!=null){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } } }
package day07chat;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.Socket;import java.util.Scanner;/** * 聊天室客户端 * * java.net.Socket * 封装了TCP协议的Socket.通过它来连接服务端的ServerSocket, * 并创建输入输出流来与服务器通信. */public class Client { //用来与服务端通信的Socket private Socket socket; /** * 构造方法,用来初始化客户端 * 构造方法通常用来初始化对象属性等操作 */ public Client(){ try{ /* * 初始化Socket时需要传入两个参数 * 1:服务端的IP地址 * 2:服务端的端口号 * * 首先要清楚: * 通讯是客户端计算机的一个客户端应用程序与服务端 * 计算机(俗称服务器)上的一个服务端应用程序之间的通讯. * * IP地址的作用是让我们通过网络可以找到服务器 * 而端口可以让我们找到运行在服务器上的服务端应用程序. * * 创建Socket实例的过程就是与服务端连接的过程,若可以 * 成功与服务器连接上,则会创建Socket实例,否则构造 * 方法会抛异常. */ System.out.println("正在尝试连接服务端..."); //本机ip:192.168.202.13 socket = new Socket("localhost",8088); System.out.println("与服务端连接成功!"); }catch(Exception e){ e.printStackTrace(); } } /** * 客户端开始工作的方法 */ public void start(){ try{ /* * 当客户端启动后,就启动接收服务段发送过来 * 消息的线程. */ GetServerMessageHandler handler = new GetServerMessageHandler(); Thread t = new Thread(handler); t.start(); /* * OutputStream getOutputStream() * Socket提供了该方法,用来获取输出流来向服务端发送数据. */ OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8"); PrintWriter pw = new PrintWriter(osw,true); Scanner scan = new Scanner(System.in); String str = ""; System.out.println("请输入..."); while(!str.equalsIgnoreCase("exit")){ str = scan.nextLine().trim(); pw.println(str); //System.out.println(br.readLine()); } pw.close(); }catch(Exception e){ } } public static void main(String[] args){ Client client = new Client(); client.start(); } /** * 由于接收服务端发送过来的消息,与我们给定服务端发送消息 * 没有必然关系,所以两者应当在两个不同的线程上完成,各做 * 各的,互不干涉. */ private class GetServerMessageHandler implements Runnable{ public void run() { try{ /* * 该线程的职责就是读取服务端发送过来的 * 每一条消息,并输出到控制台. */ InputStream in = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(in,"UTF-8"); BufferedReader br = new BufferedReader(isr); String message = null; while((message=br.readLine())!=null){ System.out.println(message); } }catch(Exception e){ e.printStackTrace(); } } }}
转载地址:http://ygews.baihongyu.com/