在古代,由于通信不便利,一些聪明的人就利用鸽子会飞且飞得比较快、会辨认方向的优点,对其进行了驯化,用来进行消息的传递——也就是所谓的“飞鸽传书”。而在 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:飞鸽传书的网络套接字的更多相关文章

  1. 自己用java实现飞鸽传书 1 - 实现socket通信

    第一步: 建立服务端客户端,实现端到端通信.因为要传递文件,信号量较大,故使用TCP/IP协议. 服务端和客户端都要建立socket,而后通过socket进行通信.目前只实现服务端到客户端的单向通信. ...

  2. 自己用java实现飞鸽传书 2 - 实现文件传输

    第二步:实现文件传递. 上一步只是从服务端传递了一个字符串到客户端,这次需要对代码进行调整,实现从服务端获取文件,在客户端将文件存入目标地址. 调整后的代码: 服务端: import java.io. ...

  3. Pyhont 网络编程【第一篇】初始Socket网络套接字

    一.什么是socket: Socket 别名 “网络套接字”,指网络通信链句柄 其实就是一堆网络信息(ip+端口) 建立起的链接称之为socket,Socket的英文原义是“孔”或“插座”,用来实现不 ...

  4. 什么是网络套接字(Socket)?

    什么是网络套接字(Socket)?一时还真不好回答,而且网络上也有各种解释,莫衷一是.下文将以本人所查阅到的资料来说明一下什么是Socket. Socket定义 Socket在维基百科的定义: A n ...

  5. ubuntu下安装飞鸽传书

    1.从官网下载Linux版本飞鸽传书(http://www.ipmsg.org.cn/) 2.解压后执行 ./QIpmsg 若报错 libstdc++.so.6: version `CXXABI_AR ...

  6. [Android源码]Android源码之高仿飞鸽传书WIFI热点搜索与创建(一)

    (本文详情来源:android源码 http://www.eoeandroid.com/thread-296427-1-1.html   转载请注明出处!)  [Android源码分享]飞鸽传书的An ...

  7. 飞鸽传书linux进程退出不彻底

    问题描述: 飞鸽传书linux版本(QIpmsg)是有问题的. 在ubuntu14.04上运行的时候,没有任务栏图标,点击关闭也不能退出进程,端口仍然占用,无法再次运行. 这个问题截至1.2.1412 ...

  8. Mac 10.12安装飞鸽传书IPMessager

    说明:这个版本的飞鸽传书不能和Linux的互通,但是可以和Windows的互通,我猜测是协议问题:如果想要互通只能是Mac和Linux同时安装iptux. 下载: (链接: https://pan.b ...

  9. sanic官方文档解析之Custom Protocols(自定义协议)和Socket(网络套接字)

    1,Custom Protocol:自定义协议 温馨提示:自定义协议是一个高级用法,大多数的读者不需要用到此功能 通过特殊的自定义协议,你可以改变sanic的协议,自定义协议需要继承子类asyncio ...

随机推荐

  1. docker-compose exec时 出现"fork/exec /proc/self/exe: no such file or directory" 报错

    问题:跟往常一样执行docker-compos exec redis sh时出现如下错误,而容器是运行状态中. # docker-compose exec redis sh rpc error: co ...

  2. 利用Docker搭建Redis集群

    Redis集群搭建 运行Redis镜像 分别使用以下命令启动3个Redis docker run --name redis-6379 -p 6379:6379 -d hub.c.163.com/lib ...

  3. springboot项目问题记录one

    上面三个方法描述如下: 首先有个业务,我是需要调取第三方一个sdk,然后sdk里面封装的kafka,也就是说,需要用sdk内置的连接kafka去消费消息,然后又有一个类需要实现Message,此Mes ...

  4. Mybatis方法入参处理

    1,在单个入参的情况下,mybatis不做任何处理,#{参数名} 即可,甚至连参数名都可以不需要,因为只有一个参数,或者使用 Mybatis的内置参数 _parameter. 2,多个入参: 接口方法 ...

  5. 查询表格——建立动态表格,使用ajax输入查询条件将后台数据查询出来以表格的形式展示出来

    建立动态表格,使用ajax将前台查询条件传给后台,并将查询结果以表格的形式展示出来. 页面的展示效果如下图所示: 第一步:查询条件的部分: 代码如下: <div class="text ...

  6. python的socket模块

    sk.bind(address) s.bind(address) 将套接字绑定到地址.address地址的格式取决于地址族.在AF_INET下,以元组(host,port)的形式表示地址. sk.li ...

  7. luogu题解 P5022 【旅行】

    本人的代码可以说洛谷最简单的了 我的存图方式有些与众不同 a[5000][5000]中第一个下标表示第几个点,第二个表示与点相连的点 虽然比前向星废内存但时间极快,大概是O(n)的. 现在步入正题 6 ...

  8. 手写C语言字符库

    鉴于以前碰到过很多这样的题目,甚至上次月考核也考了,马上就要考试了,就再重新写一遍,加深印象,但是肯定和库函数有区别,丢失许多细节 1.strlen函数(求字符串长度) int strlen(char ...

  9. 基于kubernetes自研容器管理平台的技术实践

    一.容器云的背景 伴随着微服务的架构的普及,结合开源的Dubbo和Spring Cloud等微服务框架,宜信内部很多业务线逐渐了从原来的单体架构逐渐转移到微服务架构.应用从有状态到无状态,具体来说将业 ...

  10. 【iOS】containsString iOS7 报错

    前几天发现了这个问题,原来是因为 containsString 只支持 iOS8.0 以后的系统,不支持 7... 有些地方可以用其他方法替代,如下: NSString *urlString = [[ ...