Java Socket:飞鸽传书的网络套接字
在古代,由于通信不便利,一些聪明的人就利用鸽子会飞且飞得比较快、会辨认方向的优点,对其进行了驯化,用来进行消息的传递——也就是所谓的“飞鸽传书”。而在 Java 中,网络套接字(Socket)扮演了同样的角色。
套接字(Socket)是一个抽象层,应用程序可以通过它发送或接收数据;就像操作文件那样可以打开、读写和关闭。套接字允许应用程序将 I/O 应用于网络中,并与其他应用程序进行通信。网络套接字是 IP 地址与端口的组合。
01、ping 与 telnet
“老王啊,能不能帮我看一下这个问题呢,明明本地可以进行网络通信,可等我部署到服务器上时就通信不了了,搞了半天也不知道什么原因,我看代码是没有问题的。”小二的语气中充满了沮丧。
“ping 过吗?或者 telnet 了吗?”老王头都没回,冷冰冰地扔出去了这句话。
“哦,我去试试。”小二心头掠过一丝愧疚。
ping 与 telnet 这两个命令,对调试网络程序有着非常大的帮助。
ping,一种计算机网络工具,用来测试数据包能否透过 IP 协议到达特定主机。ping 会向目标主机发出一个 ICMP 的请求回显数据包,并等待接收回显响应数据包。
例如,我们 ping 一下博客园。截图如下。
telnet,Internet 远程登录服务的标准协议和主要方式,可以让我们坐在家里的计算机面前,登录到另一台远在天涯海角的远程计算机上。
在 Windows 系统中,telnet 一般是默认安装的,但未激活(可以在控制面板中激活它)。
例如,我们 telnet 一下火(shui)土(mu)社区。截图如下。
使用 telnet 登录远程计算机时,需要远程计算机上运行一个服务,它一直不停地等待那些希望和它进行连接的网络请求;当接收到一个客户端的网络连接时,它便唤醒正在监听网络连接请求的服务器进程,并为两者建立连接。连接会一直保持,直到某一方中止。
不过,需要注意的是,telnet 在格外重视安全的现代网络技术中并不受到重用。因为 telnet 是一个明文传输协议,用户的所有内容(包括用户名和密码)都没有经过加密,安全隐患非常大。
02、Socket 实例
不知道你有没有体验一下 telnet 火土社区的那条命令,结果非常有趣。我们也可以通过 Java 的客户端套接字(Socket)实现,代码示例如下。
try (Socket socket = new Socket("bbs.newsmth.net", 23);) {
InputStream is = socket.getInputStream();
Scanner scanner = new Scanner(is, "gbk");
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
System.out.println(line);
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
1)建立套接字连接非常简单,只需要一行代码:
Socket socket = new Socket(host, port)
host 为主机名,port 为端口号(23 为默认的 telnet 端口号)。如果无法确定主机的 IP 地址,则抛出 UnknownHostException
异常;如果在创建套接字时发生 IO 错误,则抛出 IOException
异常。
需要注意的是,套接字在建立的时候,如果远程主机不可访问,这段代码就会阻塞很长时间,直到底层操作系统的限制而抛出异常。所以一般会在套接字建立后设置一个超时时间。
Socket socket = new Socket(...);
socket.setSoTimeout(10000); // 单位为毫秒
2)套接字连接成功后,可以通过 java.net.Socket
类的 getInputStream()
方法获取输入流。有了 InputStream
对象后,可以借助文本扫描器类(Scanner)将其中的内容打印出来。
InputStream is = socket.getInputStream();
Scanner scanner = new Scanner(is, "gbk");
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
System.out.println(line);
}
部分结果(完整结果自己亲手实践一下哦)如下图所示:
03、ServerSocket 实例
接下来,我们模拟一个远程服务,通过 java.net.ServerSocket
实现。代码示例如下。
try (ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
Scanner scanner = new Scanner(is)) {
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);
pw.println("你好啊,欢迎关注「沉默王二」 公众号,回复关键字「2048」 领取程序员进阶必读资料包");
boolean done = false;
while (!done && scanner.hasNextLine()) {
String line = scanner.nextLine();
System.out.println(line);
if ("2048".equals(line)) {
done = true;
}
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
1)建立服务器端的套接字也比较简单,只需要指定一个能够独占的端口号就可以了(0~1023 这些端口都已经被系统预留了)。
ServerSocket server = new ServerSocket(8888);
2)调用 ServerSocket 对象的 accept()
等待客户端套接字的连接请求。一旦监听到客户端的套接字请求,就会返回一个表示连接已建立的 Socket 对象,可以从中获取到输入流和输出流。
Socket socket = server.accept();
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
客户端套接字发送的所有信息都会包裹在服务器端套接字的输入流中;而服务器端套接字发送的所有信息都会包裹在客户端套接字的输出流中。
3)服务器端可以通过以下代码向客户端发送消息。
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os, "gbk"), true);
pw.println("你好啊,欢迎关注「沉默王二」 公众号,回复关键字「2048」 领取程序员进阶必读资料包");
4)服务器端可以通过以下代码读取客户端发送过来的消息。
Scanner scanner = new Scanner(is);
boolean done = false;
while (!done && scanner.hasNextLine()) {
String line = scanner.nextLine();
System.out.println(line);
if ("2048".equals(line)) {
done = true;
}
}
运行该服务后,可以通过 telnet localhost 8888
命令连接该远程服务,不出所料,你将会看到以下信息。
PS:可以在当前命令窗口中输入 2048,服务端收到该消息后会中断该套接字连接(当前窗口会显示“遗失对主机的连接”)。
04、为多个客户端服务
非常遗憾的是,上面的例子中,服务器端只能为一个客户端服务——这不符合服务器端一对多的要求。
优化方案也非常简单(你应该也能想得到):服务器端接收到客户端的套接字请求时,可以启动一个线程来处理,而主程序继续等待下一个连接。代码示例如下。
try (ServerSocket server = new ServerSocket(8888)) {
while (true) {
Socket socket = server.accept();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 套接字处理程序
}
});
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
线程内部(run(){}
方法里)用来处理套接字,代码示例如下:
try {
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
Scanner scanner = new Scanner(is);
// 其他代码省略
// 向客户端发送消息
// 读取客户端发送过来的消息
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
服务器端代码优化后重新运行,你就可以通过 telnet 命令测试了。打开一个命令行窗口输入 telnet localhost 8888
,再打开一个新的命令行窗口输入 telnet localhost 8888
,多个窗口都可以和服务器端进行通信,除非服务器端代码中断运行。
05、最后
如今大多数基于网络的软件,如浏览器、即时通讯工具甚至是 P2P 下载都是基于 Socket 实现的,所以掌握 Java Socket 编程还是蛮有必要的。Socket 编程也比较有趣,很多初学者都会编写一两个基于“客户端-服务器端”的小程序来提高自己的编程水平,建议你也试一试。
Java Socket:飞鸽传书的网络套接字的更多相关文章
- 自己用java实现飞鸽传书 1 - 实现socket通信
第一步: 建立服务端客户端,实现端到端通信.因为要传递文件,信号量较大,故使用TCP/IP协议. 服务端和客户端都要建立socket,而后通过socket进行通信.目前只实现服务端到客户端的单向通信. ...
- 自己用java实现飞鸽传书 2 - 实现文件传输
第二步:实现文件传递. 上一步只是从服务端传递了一个字符串到客户端,这次需要对代码进行调整,实现从服务端获取文件,在客户端将文件存入目标地址. 调整后的代码: 服务端: import java.io. ...
- Pyhont 网络编程【第一篇】初始Socket网络套接字
一.什么是socket: Socket 别名 “网络套接字”,指网络通信链句柄 其实就是一堆网络信息(ip+端口) 建立起的链接称之为socket,Socket的英文原义是“孔”或“插座”,用来实现不 ...
- 什么是网络套接字(Socket)?
什么是网络套接字(Socket)?一时还真不好回答,而且网络上也有各种解释,莫衷一是.下文将以本人所查阅到的资料来说明一下什么是Socket. Socket定义 Socket在维基百科的定义: A n ...
- ubuntu下安装飞鸽传书
1.从官网下载Linux版本飞鸽传书(http://www.ipmsg.org.cn/) 2.解压后执行 ./QIpmsg 若报错 libstdc++.so.6: version `CXXABI_AR ...
- [Android源码]Android源码之高仿飞鸽传书WIFI热点搜索与创建(一)
(本文详情来源:android源码 http://www.eoeandroid.com/thread-296427-1-1.html 转载请注明出处!) [Android源码分享]飞鸽传书的An ...
- 飞鸽传书linux进程退出不彻底
问题描述: 飞鸽传书linux版本(QIpmsg)是有问题的. 在ubuntu14.04上运行的时候,没有任务栏图标,点击关闭也不能退出进程,端口仍然占用,无法再次运行. 这个问题截至1.2.1412 ...
- Mac 10.12安装飞鸽传书IPMessager
说明:这个版本的飞鸽传书不能和Linux的互通,但是可以和Windows的互通,我猜测是协议问题:如果想要互通只能是Mac和Linux同时安装iptux. 下载: (链接: https://pan.b ...
- sanic官方文档解析之Custom Protocols(自定义协议)和Socket(网络套接字)
1,Custom Protocol:自定义协议 温馨提示:自定义协议是一个高级用法,大多数的读者不需要用到此功能 通过特殊的自定义协议,你可以改变sanic的协议,自定义协议需要继承子类asyncio ...
随机推荐
- [01] HEVD 双机调试环境搭建
[01] HEVD 双机调试环境搭建 span::selection, .CodeMirror-line > span > span::selection { background: #d ...
- 基于 Jmeter 的 web 端接口自动化测试平台(转载)
框架 功能点 批量导入har包生成接口用例 接口用例分组树形展示 用例编辑失焦自动保存 用例编写提供快捷调试模块 支持多类型参数,json+文本两种校验 支持同一个项目中接口参数传递 提供即时任务+每 ...
- 数据库触发器_trigger
部门表_删除: USE [test] GO /****** Object: Trigger [dbo].[部门_Delete] Script Date: 2015/8/31 16:41:46 **** ...
- (转)android自定义控件
原帖地址:http://my.oschina.net/wangjunhe/blog/99764 创建新的控件: 作为一个有创意的开发者,你经常会遇到安卓原生控件无法满足你的需求. 为了优化你的界面和工 ...
- C#中用WMI实现对驱动的查询
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- SpringBoot解决cors跨域问题
1.使用@CrossOrigin注解实现 (1).对单个接口配置CORS @CrossOrigin(origins = {"*"}) @PostMapping("/hel ...
- MySql的数据库优化到底优啥了都??(2)
嘟嘟在写此篇文章之前心里先默念(简单,通俗,工整)*10 吟唱完了,进入正题 3.Memory存储引擎 除了存放一个表结构相关信息的.frm文件在磁盘上,其他数据都存储在内存中.说白点哪天你数据库死机 ...
- [PTA] 数据结构与算法题目集 6-2 顺序表操作集
//创建并返回一个空的线性表: List MakeEmpty() { List L; L = (List)malloc(sizeof(struct LNode)); L->Last = -1; ...
- 搭建PowerDNS+LAP+NFS+MySQL主从半节点同步实现LAMP架构
实验环境:(共7台机器) PowerDNS: 192.168.99.110 两台LAP: 192.168.99.120 和 192.168.99.130 NFS服务器:192.168.99.14 ...
- 【iOS】PLA 3.3.12
发件人 Apple Program License Agreement PLA We found that your app uses the Advertising Identifier but d ...