[Java]Socket套接字(网络编程入门)
【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://www.cnblogs.com/cnb-yuchen/p/18032037
出自【进步*于辰的博客】
参考笔记二,P61。
注:“一对一”、“多对多”是相对于
Socket
而言,而非服务端 / 客户端类的个数。
介绍
两种聊天模式的共同点:Client
和Server
都是以管道(IO流)的方式进行一对一连接、通信,故在"多对多"聊天模式的实现中,需要循环接收多个Socket
(客户端,见第2.2项)。(大家可能云里雾里,继续看)
从IO流的角度:
Client
,读取input
流是获取回复,output
流用于发送请求;Server
:读取input
是获取请求,output
流用于响应。
Client
与 Server
发送消息的实现都基于阻塞,实现“聊天”的本质就是循环“发送-接收”消息的过程。
两种聊天模式
“一对一”
1、服务端。
/** * 功能:Server 与 Client 一对一聊天 */ public class Server { public static void main(String[] args) throws Exception { ServerSocket server = new ServerSocket(8844); System.out.println("等待:"); Socket client = server.accept();// 阻塞,当有 Socket 访问时,返回此 Socket,阻塞放开 System.out.println("连接成功"); BufferedReader req = new BufferedReader(new InputStreamReader(client.getInputStream()));// 缓冲输入流,用于获取请求 PrintWriter resp = new PrintWriter(client.getOutputStream(), true);// 缓冲输出流,用于响应。true → 自动刷新 BufferedReader in = new BufferedReader(new InputStreamReader(System.in));// 用于获取键盘输入 while (true) { String reqStr = req.readLine();// 读取 Socket 输入流,因为 Client 还未输入,阻塞。当 Client 输入后,阻塞放开 System.out.println("请求 = " + reqStr); System.out.print("input:"); String respStr = in.readLine();// 获取键盘输入,阻塞 resp.println(respStr);// 输出响应(发送回复) } } }
2、客户端。
public class Client { public static void main(String[] args) throws Exception { Socket client = new Socket("127.0.0.1", 8844); BufferedReader in = new BufferedReader(new InputStreamReader(System.in));// 缓冲输入流,用于获取键盘输入 PrintWriter req = new PrintWriter(new OutputStreamWriter(client.getOutputStream()), true);// 缓冲输出流,用于发送请求 BufferedReader resp = new BufferedReader(new InputStreamReader(client.getInputStream()));// 用于获取响应 while (true) { System.out.print("input:"); String reqStr = in.readLine();// 获取键盘输入,阻塞 req.println(reqStr);// 发送请求 String respStr = resp.readLine();// 读取输入流(获取响应) System.out.println("响应 = " + respStr); } } }
127.0.0.1
是本地ip
,如果服务端不在本地,可以使用cmd → ipconfig 或 ipconfig/all
查询本机ip
。
“多对多”
1、服务端。
/** * 业务:负责将各个客户端发送的信息广播推送 */ public class Server { private static Set<Socket> set = Collections.synchronizedSet(new HashSet<>()); public static void main(String[] args) throws Exception { ServerSocket server = new ServerSocket(8844); System.out.println("等待:"); while (true) { Socket client = server.accept(); System.out.println("连接成功"); set.add(client); /** * 为什么使用线程? * 1、实时接收各个客户端消息; * 2、实时推送; * 3、为了能获取各个客户端信息。 * 这些工作只能在后台运行,而不能在主线程执行,因为一个线程(主线程)会因阻塞、导致后续其他工作一并终止。 * 因此,需要线程,并且为每个客户端都创建一个。 */ new Thread(() -> { /** * 为什么在线程内获取输入流等信息,而不是线程外? * 因为线程外属于主线程,如 req 这些变量都是局部变量,会被后续覆盖, * 因此置于线程内,这样每个线程都独立保存着当前客户端的信息。 */ try (BufferedReader req = new BufferedReader(new InputStreamReader(client.getInputStream()))) { while (true) { String reqStr = req.readLine(); for (Socket current : set) { if (current == client)// 向所有客户端推送消息,排除发送者 continue; // 根据当前客户端对象获取输出管道(输出流)推送消息 PrintWriter resp = new PrintWriter(current.getOutputStream(), true); resp.println(reqStr); } System.out.println(new Date()); } } catch (Exception e) { e.printStackTrace(); } }).start(); } } }
2、客户端。
public class Client { public static void main(String[] args) throws Exception { Socket client = new Socket("127.0.0.1", 8844); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); PrintWriter req = new PrintWriter(new OutputStreamWriter(client.getOutputStream()), true); BufferedReader resq = new BufferedReader(new InputStreamReader(client.getInputStream())); /** * 为什么使用线程? * 为了实时获取推送消息 */ new Thread(() -> { try { while (true) { String respStr = resq.readLine(); System.out.println("响应 = " + respStr); } }catch (Exception e) { e.printStackTrace(); } }).start(); // 发送请求 while (true) { System.out.println("input:"); String reqStr; if (!(reqStr = in.readLine()).isEmpty()) req.println(reqStr); else System.out.println("info can't empty"); } } }
若要生成多个客户端进行聊天,正确的做法不是重启类,因为那样的结果是覆盖,结果还是一个客户端。因此,需要再创建Client
类。
最后
本文的例子是为了阐述Socket
套接字的使用、方便大家理解而简单举出的,不一定有实用性,仅是抛砖引玉。并且,示例功能比较粗糙。为什么我不添加一些功能?比如:使用swing
或html
生成一个聊天界面、完善聊天功能,等等。
缘由:
- 本文的核心是阐述
Socket
套接字的使用; Socket
套接字只是网络编程的入门;- 单一使用
Socket
难以开发性能优良、功能完善的聊天功能。
因此,后续我会使用WebSocket
实现。
本文完结。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性