我的编程空间,编程开发者的网络收藏夹
学习永远不晚

菜鸟初学Java的备忘录(六) (转)

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

菜鸟初学Java的备忘录(六) (转)

菜鸟初学Java的备忘录(六) (转)[@more@]

2003年1月21日 星期二 晴
通过程序建立了实际的概念之后,现在应该回到最开始的问题,Socket是什么?是实现计算机通信的一种方式,这毫无疑问.但如何能够用最容易理解的语言比较形象而又不偏颇的描述它的原理呢?

Bruce Eckel 在他的《Java 编程思想》一书中这样描述套接字:
套接字是一种软件抽象,用于表达两台机器之间的连接“终端”。对于一个给定的连接,每台机器上都有一个套接字,您也可以想象它们之间有一条虚拟的“电缆”,“电缆”的每一端都插入到套接字中。当然,机器之间的物理硬件和电缆连接都是完全未知的。抽象的全部目的是使我们无须知道不必知道的细节.

 
按我的理解,抽象点来说,一个Socket就是一个电话听筒,你有一个,和你通话的人也有一个,只不过其中有一个人的听筒叫ServerSocket,另一个人的听筒叫Socket.至于谁是ServerSocket,谁是Socket,这不重要,因为客户端和服务器端本来就是相对的,可以互相转化的.通话的两个人通过拿起两个听筒建立了一条通道,这条通道通不通就要看是不是双方都拿起听筒了,假如只有一方拿起听筒,那就只能听到一些嘟嘟的声音,证明通道不同.这里,拿起听筒的过程就是Socket初始化的过程.建立了通道之后,也就是大家都拿起听筒之后,通道两端的人就可以开始通话了.这里又有两个过程,即A对B说话,B接听,和B对A说话,A收听,这两个过程是通过两条线路完成的.传输在这两条线路上的,就是流.流隐藏了所有传输的细节,使得通信双方都认为,他们传过去的是声音,而不是编码.


前面写的服务器端的程序实际上是单任务版本,服务器对客户机的处理机制是在同一时间段内只能处理一个连接,因为handleConnection中采取的是不断循环的阻塞方法,检测到一个,就处理一个,然后再检测到一个,就再处理一个,如果有多个连接同时请求,那只能排队等候.这样的程序是无法在网络中应付多个连接的,因为你无法保证在同一时间内只有一个客户提出与服务器的连接请求,而用阻塞的方法应付多客户连接其速度之慢是可想而知的.

这样就催生了面向多连接的版本.显然,通过多线程可以来实现我们的要求.

由于要解决的是处理客户连接的问题,因此我们的工作只是在服务器端的程序当中修改.其原理不难推出,就是在检测到一个连接请求之后,马上建立一个线程去处理它,然后继续兼听下一个连接请求.所以,我们只需要将原来在handleConnection中的代码原封不动的放到线程的执行代码中,而在handleConnection中添加上新建线程的代码就可以了,十分简单.

同上一篇的风格一样,我们来观察各个部分的代码细节.
首先为这个多线程的版本创建类MultiThreadRemoteFileServer

看看这个类的定义
import java.io.*;
import java.NET.*;

public class MultiThreadRemoteFileServer{
  protected int listenPort;
  public MultiThreadRemoteFileServer(int aListenPort){
  }
  public static void main(String[] args) {
  }
  public void acceptConnections() {
  }
  public void handleConnection(Socket incomingConnection) {
  }
}

几乎和RemoteFileServer是一样的,唯一的区别是在我们现在创建的这个类中增加了一个构造函数,这是为了能够使得监听的端口号由我们自己来定.定义如下

public MultithreadedRemoteFileServer(int aListenPort) {
  listenPort = aListenPort;
}


先来看main()
public static void main(String[] args) {
  MultithreadedRemoteFileServer server = new MultithreadedRemoteFileServer(3000);
  server.acceptConnections();
}

没有区别吧,和RemoteFileServer的main()函数,只是端口号在创建的时候由主程序指定而已。

我们主要关心的改动都在后面
现在看acceptConnection监听程序
public void acceptConnections() {
  try {
  ServerSocket server = new ServerSocket(listenPort, 5);//注意到没有,建立服务器Socket的时候多了一个参数,这个参数是用来指定能够同时申请连接的最大数目,缺省值是50
  Socket incomingConnection = null;
  while (true) {
  incomingConnection = server.accept();
  handleConnection(incomingConnection);
  }
  } catch (BindException e) {
  System.out.println("Unable to bind to port " + listenPort);
  } catch (IOException e) {
  System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
  }
}

改动的地方就一个,多了个参数.这里是它的工作机制。假设我们指定待发数(backlog 值)是5并且有五台客户机请求连接到我们的服务器。我们的服务器将着手处理第一个连接,但处理该连接需要很长时间。由于我们的待发值是 5,所以我们一次可以放五个请求到队列中。我们正在处理一个,所以这意味着还有其它五个正在等待。等待的和正在处理的一共有六个。当我们的服务器仍忙于接受一号连接(记住队列中还有 2—6 号)时,如果有第七个客户机提出连接申请,那么,该第七个客户机将遭到拒绝


接着看,我们的下一个改动显然是在处理监听到的线程的方法handleConnection中,前面已经说了,在多线程的版本中,我们检测到一个连接请求,就马上生成一个线程,然后就不用理它了,那么在这里就是新建线程的一句话.

public void handleConnection(Socket connectionToHandle) {
  new Thread(new ConnectionHandler(connectionToHandle)).start();
}

我们注意到有一个新的类ConnectionHandler,这个类是Runnable的,即是一个接口类(这是用接口实现的一个线程,要是有不明白的话,可以去看看17号的关于线程的东西).我们用 ConnectionHandler 创建一个新 Thread 并启动它。正如我们刚才所说的,原来在RemoteFileServer的handleConnection中的代码统统原封不动的转移到了这个接口类ConnectionHandler的run()方法中来了.

那么我们来看看整个ConnectionHandler类的定义吧。

class ConnectionHandler implements Runnable {
  protected Socket socketToHandle;
  public ConnectionHandler(Socket aSocketToHandle) {
  socketToHandle = aSocketToHandle;//通过构造函数,将待处理的Socket实例作为参数传送进来
  }
  public void run() {//原来对Socket的读/写的代码都在这里了
  try {
  PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream());
  BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));

  String fileToRead = streamReader.readLine();
  BufferedReader fileReader = new BufferedReader(new FileReader(fileToRead));

  String line = null;
  while ((line = fileReader.readLine()) != null)
  streamWriter.println(line);

  fileReader.close();
  streamWriter.close();
  streamReader.close();
  } catch (Exception e) {
  System.out.println("Error handling a client: " + e);
  }
  }
}


ConnectionHandler 的 run() 方法所做的事情就是 RemoteFileServer 上的 handleConnection() 所做的事情。首先把 InputStream 和 OutputStream 分别包装(用 Socket 的 getOutputStream() 和 getInputStream())进 BufferedReader 和 PrintWriter。然后我们用这些代码逐行地读目标文件.由于InputStream中装的是文件路径,所以中间还需要使用FileReader流将文件路径包装,再经由BufferedReader包装读出.


我们的多线程服务器研究完了,同样,我们回顾一下创建和使用“多线程版”的服务器的步骤:

1.修改 acceptConnections() 以用缺省为 50(或任何您想要的大于 1 的指定数字)实例化 ServerSocket。

2. 修改 ServerSocket 的 handleConnection() 以用 ConnectionHandler 的一个实例生成一个新的 Thread。

3.借用 RemoteFileServer 的 handleConnection() 方法的代码实现 ConnectionHandler 类的run()函数。


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

菜鸟初学Java的备忘录(六) (转)

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

菜鸟初学Java的备忘录(六) (转)

菜鸟初学Java的备忘录(六) (转)[@more@]2003年1月21日 星期二 晴通过程序建立了实际的概念之后,现在应该回到最开始的问题,Socket是什么?是实现计算机通信的一种方式,这毫无疑问.但如何能够用最容易理解的语言比较形象而
2023-06-03

菜鸟初学Java的备忘录(十) (转)

菜鸟初学Java的备忘录(十) (转)[@more@]2003年1月26日 星期日 雨回顾一下昨天所学习的基于连接池的Socket,其原理中的要点如下:服务器开放有限个PooledConnectionHandler线程用来处理连接;客户的连
2023-06-03

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录