think in java 读书笔记 2 —— 套接字
目录
think in java 读书笔记 1 ——移位
think in java 读书笔记 2 —— 套接字
think in java 读书笔记 3 —— 数据报
概要
1. 套接字基本知识
2. 交互过程
3. 一个简单的服务器和客户机程序
4. 服务多个客户
有关套接字的详细介绍,可以看下《think in java》15.2章节。
1. 套接字基本知识
“套接字”或者“插座”(Socket)也是一种软件形式的抽象,用于表达两台机器间一个连接的“终端”。针对一个特定的连接,每台机器上都有一个“套接字”,可以想象它们之间有一条虚拟的“线缆”。线缆的每一端都插入一个“套接字”或者“插座”里。当然,机器之间的物理性硬件以及电缆连接都是完全未知的。抽象的基本宗旨是让我们尽可能不必知道那些细节。
在Java 中,我们创建一个套接字,用它建立与其他机器的连接。从套接字得到的结果是一个InputStream 以及OutputStream(若使用恰当的转换器,则分别是Reader 和Writer),以便将连接作为一个IO 流对象对待。有两个基于数据流的套接字类:ServerSocket,服务器用它“侦听”进入的连接;以及Socket,客户用它初始一次连接。一旦客户(程序)申请建立一个套接字连接,ServerSocket 就会返回(通过accept()方法)一个对应的服务器端套接字,以便进行直接通信。从此时起,我们就得到了真正的“套接字-套接字”连接,可以用同样的方式对待连接的两端,因为它们本来就是相同的!此时可以利用getInputStream()以及getOutputStream()从每个套接字产生对应的InputStream 和OutputStream 对象。这些数据流必须封装到缓冲区内。
对于Java 库的命名机制,ServerSocket(服务器套接字)的使用无疑是容易产生混淆的又一个例证。大家可能认为ServerSocket 最好叫作“ServerConnector”(服务器连接器),或者其他什么名字,只是不要在其中安插一个“Socket”。也可能以为ServerSocket 和Socket 都应从一些通用的基础类继承。事实上,这两种类确实包含了几个通用的方法,但还不够资格把它们赋给一个通用的基础类。相反,ServerSocket 的主要任务是在那里耐心地等候其他机器同它连接,再返回一个实际的Socket。这正是“ServerSocket”这个命名不恰当的地方,因为它的目标不是真的成为一个Socket,而是在其他人同它连接的时候产生一个Socket 对象。
然而,ServerSocket 确实会在主机上创建一个物理性的“服务器”或者侦听用的套接字。这个套接字会侦听进入的连接,然后利用accept()方法返回一个“已建立”套接字(本地和远程端点均已定义)。容易混淆的地方是这两个套接字(侦听和已建立)都与相同的服务器套接字关联在一起。侦听套接字只能接收新的连接请求,不能接收实际的数据包。所以尽管ServerSocket 对于编程并无太大的意义,但它确实是“物理性”的。
创建一个ServerSocket 时,只需为其赋予一个端口编号。不必把一个IP 地址分配它,因为它已经在自己代表的那台机器上了。但在创建一个Socket 时,却必须同时赋予IP 地址以及要连接的端口编号(另一方面,从ServerSocket.accept()返回的Socket 已经包含了所有这些信息)。
2. 交互过程
连接建立好后,服务器端和客户端的输入流和输出流就互为彼此,即一端的输出流是另一端的输入流。
3. 一个简单的服务器和客户机程序
这个例子将以最简单的方式运用套接字对服务器和客户机进行操作。服务器的全部工作就是等候建立一个连接,然后用那个连接产生的Socket 创建一个InputStream 以及一个OutputStream。在这之后,它从InputStream 读入的所有东西都会反馈OutputStream,直到接收到行中止(END)为止,最后关闭连接。客户机连接与服务器的连接,然后创建一个OutputStream。文本行通过OutputStream 发送。客户机也会创建一个InputStream,用它收听服务器说些什么(本例只不过是反馈回来的同样的字句)。服务器与客户机(程序)都使用同样的端口号,而且客户机利用本地主机地址连接位于同一台机器中的服务器(程序),所以不必在一个物理性的网络里完成测试(在某些配置环境中,可能需要同真正的网络建立连接,否则程序不能工作——尽管实际并不通过那个网络通信)。
下面是服务器程序:
1 /** 2 * @Title: JabberServer.java 3 * @Description: TODO 4 * @author :Xingle 5 * @date 2014-7-22 下午12:50:53 6 * @version 7 */ 8 9 package com.xingle_test.socket; 10 11 import java.io.BufferedReader; 12 import java.io.BufferedWriter; 13 import java.io.IOException; 14 import java.io.InputStreamReader; 15 import java.io.OutputStreamWriter; 16 import java.io.PrintWriter; 17 import java.net.ServerSocket; 18 import java.net.Socket; 19 20 /** 21 * 一个简单的服务器和客户机程序 22 * 23 * @ClassName: JabberServer TODO 24 * @author Xingle 25 * @date 2014-7-22 下午12:50:53 26 */ 27 public class JabberServer { 28 29 // Choose a port outside of the range 1-1024: 30 public static final int POST = 8080; 31 32 public static void main(String[] args) throws IOException { 33 ServerSocket s = new ServerSocket(POST); 34 System.out.println("Started :" + s); 35 try { 36 // Blocks until a connection occurs: 37 Socket socket = s.accept(); 38 try { 39 System.out.println("Connection acception:" + socket); 40 BufferedReader in = new BufferedReader(new InputStreamReader( 41 socket.getInputStream())); 42 // Output is automatically flushed by PrintWriter: 43 PrintWriter out = new PrintWriter(new BufferedWriter( 44 (new OutputStreamWriter(socket.getOutputStream()))), 45 true); 46 while (true) { 47 String str = in.readLine(); 48 if (str.equals("END")) 49 break; 50 System.out.println("Echoing:" + str); 51 out.println(str); 52 } 53 // Always close the two sockets... 54 } finally { 55 System.out.println("service closing..."); 56 socket.close(); 57 } 58 } finally { 59 s.close(); 60 } 61 } 62 }
下面是客户程序的源码:
1 /** 2 * @Title: JabberClient.java 3 * @Description: TODO 4 * @author :Xingle 5 * @date 2014-7-22 下午1:53:35 6 * @version 7 */ 8 9 package com.xingle_test.socket; 10 11 import java.io.BufferedReader; 12 import java.io.IOException; 13 import java.io.InputStreamReader; 14 import java.io.OutputStreamWriter; 15 import java.io.PrintWriter; 16 import java.net.InetAddress; 17 import java.net.Socket; 18 19 /** 20 * 21 * @ClassName: JabberClient TODO 22 * @author Xingle 23 * @date 2014-7-22 下午1:53:35 24 */ 25 public class JabberClient { 26 27 public static void main(String[] args) throws IOException { 28 // testing on one machine w/o a network: 29 InetAddress addr = InetAddress.getByName(null); 30 // Alternatively, you can use 31 // the address or name: 32 // InetAddress addr = 33 // InetAddress.getByName("127.0.0.1"); 34 // InetAddress addr = 35 // InetAddress.getByName("localhost"); 36 System.out.println("addr=" + addr); 37 Socket socket = new Socket(addr, JabberServer.POST); 38 // Guard everything in a try-finally to make sure that the socket is closed: 39 try { 40 System.out.println("socket=" + socket); 41 BufferedReader in = new BufferedReader(new InputStreamReader( 42 socket.getInputStream())); 43 // Output is automatically flushed by PrintWriter: 44 PrintWriter out = new PrintWriter(new PrintWriter( 45 new OutputStreamWriter(socket.getOutputStream())), true); 46 for (int i = 0; i < 10; i++) { 47 out.println("howdy:"+i); 48 String str = in.readLine(); 49 System.out.println(str); 50 } 51 out.println("END"); 52 } finally { 53 System.out.println("closing..."); 54 socket.close(); 55 } 56 } 57 58 }
然后先运行服务器端,再运行客户端
客户端的执行结果:
addr=localhost/127.0.0.1
socket=Socket[addr=localhost/127.0.0.1,port=8080,localport=58540]
howdy:0
howdy:1
howdy:2
howdy:3
howdy:4
howdy:5
howdy:6
howdy:7
howdy:8
howdy:9
closing...
服务器端的执行结果:
Started :ServerSocket[addr=0.0.0.0/0.0.0.0,localport=8080]
Connection acception:Socket[addr=/127.0.0.1,port=58540,localport=8080]
Echoing:howdy:0
Echoing:howdy:1
Echoing:howdy:2
Echoing:howdy:3
Echoing:howdy:4
Echoing:howdy:5
Echoing:howdy:6
Echoing:howdy:7
Echoing:howdy:8
Echoing:howdy:9
service closing...
4. 服务多个客户
最基本的方法是在服务器(程序)里创建单个ServerSocket,并调用accept()来等候一个新连接。一旦accept()返回,我们就取得结果获得的Socket,并用它新建一个线程,令其只为那个特定的客户服务。然后再调用accept() ,等候下一次新的连接请求。
对于下面这段服务器代码,可发现它与JabberServer.java 例子非常相似,只是为一个特定的客户提供服务的所有操作都已移入一个独立的线程类中:
服务器代码:
1 package com.xingle_test.socket; 2 3 import java.io.*; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 /** 8 * 服务器端 9 * @ClassName: MultiJabberServer 10 * @author Xingle 11 * @date 2014-7-23 下午9:10:23 12 */ 13 public class MultiJabberServer { 14 static final int PORT = 8080; 15 16 public static void main(String[] args) throws IOException { 17 ServerSocket s = new ServerSocket(PORT); 18 System.out.println("Server Started"); 19 try { 20 while (true) { 21 // Blocks until a connection occurs: 22 Socket socket = s.accept(); 23 try { 24 new ServeOneJabber(socket); 25 } catch (IOException e) { 26 // If it fails, close the socket, 27 // otherwise the thread will close it: 28 socket.close(); 29 } 30 } 31 } finally { 32 s.close(); 33 } 34 } 35 } 36 37 class ServeOneJabber extends Thread { 38 private Socket socket; 39 private BufferedReader in; 40 private PrintWriter out; 41 42 public ServeOneJabber(Socket s) throws IOException { 43 socket = s; 44 in = new BufferedReader(new InputStreamReader(socket.getInputStream())); 45 // Enable auto-flush: 46 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( 47 socket.getOutputStream())), true); 48 // If any of the above calls throw an 49 // exception, the caller is responsible for 50 // closing the socket. Otherwise the thread 51 // will close it. 52 start(); // Calls run() 53 } 54 55 public void run() { 56 try { 57 while (true) { 58 String str = in.readLine(); 59 if (str.equals("END")) 60 break; 61 System.out.println("Echoing: " + str); 62 out.println(str); 63 } 64 System.out.println("closing..."); 65 } catch (IOException e) { 66 } finally { 67 try { 68 socket.close(); 69 } catch (IOException e) { 70 } 71 } 72 } 73 }
和以前一样,我们创建一个ServerSocket,并调用accept()允许一个新连接的建立。但这一次,accept() 的返回值(一个套接字)将传递给用于ServeOneJabber 的构建器,由它创建一个新线程,并对那个连接进行控制。连接中断后,线程便可简单地消失。
如果ServerSocket 创建失败,则再一次通过main()掷出违例。如果成功,则位于外层的try-finally 代码块可以担保正确的清除。位于内层的try-catch 块只负责防ServeOneJabber 构建器的失败;若构建器成功,则ServeOneJabber 线程会将对应的套接字关掉。
为了证实服务器代码确实能为多名客户提供服务,下面这个程序将创建许多客户(使用线程),并同相同的服务器建立连接。每个线程的“存在时间”都是有限的。一旦到期,就留出空间以便创建一个新线程。允许创建的线程的最大数量是由final int maxthreads 决定的。大家会注意到这个值非常关键,因为假如把它设得很大,线程便有可能耗尽资源,并产生不可预知的程序错误。
客户端程序:
1 package com.xingle_test.socket; 2 3 import java.io.*; 4 import java.net.InetAddress; 5 import java.net.Socket; 6 7 /** 8 * 客户端 9 * @ClassName: MultiJabberClient 10 * @author Xingle 11 * @date 2014-7-23 下午9:13:10 12 */ 13 public class MultiJabberClient { 14 static final int MAX_THREADS = 4; 15 16 public static void main(String[] args) throws IOException, 17 InterruptedException { 18 InetAddress addr = InetAddress.getByName(null); 19 while (true) { 20 if (JabberClientThread.threadCount() < MAX_THREADS) 21 new JabberClientThread(addr); 22 Thread.currentThread().sleep(100); 23 } 24 } 25 } 26 27 class JabberClientThread extends Thread { 28 private Socket socket; 29 private BufferedReader in; 30 private PrintWriter out; 31 private static int counter = 0; 32 private int id = counter++; 33 private static int threadcount = 0; 34 35 public static int threadCount() { 36 return threadcount; 37 } 38 39 public JabberClientThread(InetAddress addr) { 40 System.out.println("Making client " + id); 41 threadcount++; 42 try { 43 socket = new Socket(addr, MultiJabberServer.PORT); 44 } catch (IOException e) { 45 // If the creation of the socket fails, 46 // nothing needs to be cleaned up. 47 } 48 try { 49 in = new BufferedReader(new InputStreamReader( 50 socket.getInputStream())); 51 // Enable auto-flush: 52 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( 53 socket.getOutputStream())), true); 54 start(); 55 } catch (IOException e) { 56 // The socket should be closed on any 57 // failures other than the socket 58 // constructor: 59 try { 60 socket.close(); 61 } catch (IOException e2) { 62 } 63 } 64 // Otherwise the socket will be closed by 65 // the run() method of the thread. 66 } 67 68 public void run() { 69 try { 70 for (int i = 0; i < 5; i++) { 71 out.println("Client " + id + ": " + i); 72 String str = in.readLine(); 73 System.out.println(str); 74 } 75 out.println("END"); 76 } catch (IOException e) { 77 } finally { 78 // Always close it: 79 try { 80 socket.close(); 81 } catch (IOException e) { 82 } 83 threadcount--; // Ending this thread 84 } 85 } 86 }
首先执行服务器端程序,再执行客户端程序,先看下客户端结果:
Making client 0
Client 0: 0
Client 0: 1
Client 0: 2
Client 0: 3
Client 0: 4
Making client 1
Client 1: 0
Client 1: 1
Client 1: 2
Client 1: 3
Client 1: 4
Making client 2
Client 2: 0
Client 2: 1
Client 2: 2
Client 2: 3
Client 2: 4
Making client 3
Client 3: 0
Client 3: 1
Client 3: 2
Client 3: 3
Client 3: 4
Making client 4
Client 4: 0
Client 4: 1
Client 4: 2
Client 4: 3
Client 4: 4
Making client 5
Client 5: 0
Client 5: 1
Client 5: 2
Client 5: 3
Client 5: 4
Making client 6
Client 6: 0
Client 6: 1
Client 6: 2
Client 6: 3
Client 6: 4
Making client 7
Client 7: 0
Client 7: 1
.
.
.
(截取一部分)
再看下服务器端console:
Server Started
Echoing: Client 0: 0
Echoing: Client 0: 1
Echoing: Client 0: 2
Echoing: Client 0: 3
Echoing: Client 0: 4
closing...
Echoing: Client 1: 0
Echoing: Client 1: 1
Echoing: Client 1: 2
Echoing: Client 1: 3
Echoing: Client 1: 4
closing...
Echoing: Client 2: 0
Echoing: Client 2: 1
Echoing: Client 2: 2
Echoing: Client 2: 3
Echoing: Client 2: 4
closing...
Echoing: Client 3: 0
Echoing: Client 3: 1
Echoing: Client 3: 2
Echoing: Client 3: 3
Echoing: Client 3: 4
closing...
Echoing: Client 4: 0
Echoing: Client 4: 1
Echoing: Client 4: 2
Echoing: Client 4: 3
Echoing: Client 4: 4
closing...
Echoing: Client 5: 0
Echoing: Client 5: 1
Echoing: Client 5: 2
Echoing: Client 5: 3
Echoing: Client 5: 4
closing...
Echoing: Client 6: 0
Echoing: Client 6: 1
Echoing: Client 6: 2
Echoing: Client 6: 3
Echoing: Client 6: 4
closing...
Echoing: Client 7: 0
Echoing: Client 7: 1
Echoing: Client 7: 2
.
.
.
(截取一部分)
JabberClientThread 构建器获取一个InetAddress,并用它打开一个套接字。大家可能已看出了这样的一个套路:Socket 肯定用于创建某种Reader 以及/或者Writer(或者InputStream 和/或OutputStream)对象,这是运用Socket 的唯一方式(当然,我们可考虑编写一、两个类,令其自动完成这些操作,避免大量重复的代码编写工作)。同样地,start()执行线程的初始化,并调用run()。在这里,消息发送给服务器,而来自服务器的信息则在屏幕上回显出来。然而,线程的“存在时间”是有限的,最终都会结束。注意在套接字创建好以后,但在构建器完成之前,假若构建器失败,套接字会被清除。否则,为套接字调用close()的责任便落到了run()方法的头上。
threadcount 跟踪计算目前存在的JabberClientThread 对象的数量。它将作为构建器的一部分增值,并在run()退出时减值(run()退出意味着线程中止)。在MultiJabberClient.main()中,大家可以看到线程的数量会得到检查。若数量太多,则多余的暂时不创建。方法随后进入“休眠”状态。这样一来,一旦部分线程最后被中止,多作的那些线程就可以创建了。大家可试验一下逐渐增大MAX_THREADS,看看对于你使用的系统来说,建立多少线程(连接)才会使您的系统资源降低到危险程度。
转载于:https://www.cnblogs.com/xingele0917/p/3860626.html
think in java 读书笔记 2 —— 套接字相关推荐
- Java读书笔记(4)-多线程(二)
Java读书笔记(4)-多线程(二) 2016-1-2 线程通信 传统的线程通信 Object类提供了wait(),notify()和notifyAll三个方法 适用情况:synchronized修饰 ...
- Java读书笔记(8)-单例模式
Java读书笔记(8)-单例模式 今天在阅读<Effective Java 2>第3条时,获知一种使用枚举enum实现单例模式的新方法,然而书上并没有就此展开深入说明,于是上网查阅了一些资 ...
- Effective Java读书笔记(二)
Effective Java 读书笔记 (二) 创建和销毁对象 遇到多个构造器参数时要考虑使用构建器 创建和销毁对象 何时以及如何创建对象? 何时以及如何避免创建对象? 如何确保它们能够适时地销毁? ...
- head first java读书笔记
head first java读书笔记 1. 基本信息 页数:689 阅读起止日期:20170104-20170215 2. 标签 Java入门 3. 价值 8分 4. 主题 使用面向对象的思路介绍J ...
- Effective Java 读书笔记(七):通用程序设计
Effective Java 读书笔记七通用程序设计 将局部变量的作用域最小化 for-each 循环优于传统的 for 循环 了解和使用类库 如果需要精确的答案请避免使用 float 和 doubl ...
- #java读书笔记#面向对象2
上一篇java读书系列笔记文章:#java读书笔记#面向对象1 14.Math类的使用(重点) (1)数学操作类:该类没有构造函数,方法均为静态的 (2)掌握内容 A:成员变量 **E:比任何其他值都 ...
- JAVA socket编程 Datagram套接字 UDP协议(转)
查看文章 JAVA socket编程 Datagram套接字 UDP协议 2009-05-13 09:35 1 UDP套接字 数据报(Datagram)是网络层数据单元在介质上传输信息的一 ...
- 《孩子,你慢慢来》的读书笔记与读后感2600字
<孩子,你慢慢来>的读书笔记与读后感2600字: 龙--保护儿童的思维: 今天读<孩子,你慢慢来>龙这一节,安安的妈妈是中国人,她在安安两岁的时候就认识到安安有着固执的个性.安 ...
- 《巴巴拉少校》读书笔记优秀范文2877字
<巴巴拉少校>读书笔记优秀范文2877字: 以对话形式,一边是救济穷人的巴巴拉救世军组织,一边是做军火商的父亲.一边是有远大理想想拯救人灵魂的巴巴拉,一边是以金钱为信仰的父亲. 德谢夫的金 ...
最新文章
- matplotlib 可视化必知必会富文本绘制方法
- Android中使用ViewStub提高布局性能
- NHibernate学习导航
- 《音乐达人秀:Adobe Audition CC实战222例》——1.3 数字录音记录生活越来越便捷...
- MYSQL SHELL 到底是个什么局 剑指 “大芒果”
- Spring-core-Environment/profiles
- html购物车结算代码,JavaScript购物车结算案例
- Java绿盾解密- Ldterm(绿盾加密文件解密)
- 那些年,我们一起踩过的 “Android 坑”
- ps使用仿制图章工具,图案图章工具
- Python图书商城(可运行代码)有说明文档
- ue4人物动作_ue4人物动作资源Resource Gathering Animation418
- 大学计算机实验报告虚拟机,安装虚拟机的实验报告(共10篇).docx
- 网络安全 顶级进行鱼叉式钓鱼攻击?手把手教学
- TMA三均线股票期货高频交易策略的R语言实现
- 形参和实参的定义与区别
- 光学共焦显微技术part 1
- JS算法:广度优先搜索(BSF)的理解
- windows快捷键+组合键+搜索命令
- 1172. Queens, Knights and Pawns
热门文章
- 腾讯电脑管家离线安装包_这个良心小工具,让你电脑流畅1倍,干掉流氓软件...
- python导入自己写的py_卧槽,神操作!一句查询让Python帮忙自己写程序
- vmare fusion:No Ethernet Connection VMware Fusion 12 macOS Big Sur
- Animoca Brands 旗下子公司 GAMEE 将于 4 月 2 日启动公募
- TokenInsight:反映区块链行业整体表现的TI指数较昨日同期下跌2.77%
- 智能风控中的全场景化的模型组合包括哪些内容
- win10下的用交叉线实现文件共享
- idea 中maven编译速度过慢的问题的解决
- 【BZOJ3930】[CQOI2015]选数 莫比乌斯反演
- 第二阶段冲刺第八天(6月7号)