套接字和网络

      最后更新:2022-06-19 12:42:51 手机定位技术交流文章

      客户端/服务器设计模式

      客户端/服务器设计模式用于通信消息,在这个模式中有两种进程:客户端和服务器。

      • 客户端通过连接到服务器开始通信。
      • 客户端向服务器发送请求,服务器答复。
      • 服务器可以同时处理多个客户端的连接,客户也可以连接到多个服务器。

      许多互联网应用程序的工作是这样:网页浏览器是网页服务器的客户端,Outlook等电子邮件程序是邮件服务器的客户端,等等。

      在互联网上,客户端和服务器进程通常运行在不同的计算机上,仅通过网络连接。

      二、套接字和流

      我们首先讨论了与网络通信有关的一些重要概念,以及一般输入/输出。 输入/输出(I/O)是指进程间的通信,它可以通过网络或文件间进行通信,也可以通过命令行或图形用户界面与用户进行通信。

      (一)IP地址

      网络接口由IP地址识别,IP版本4地址是由四个8位部分组成的32位数字。

      • 18.9.22.69这是MIT网络服务器的IP地址。
      • 173.194.193.99这是谷歌网络服务器的地址。
      • 104.47.42.36这是微软Outlook电子邮件处理程序的地址。
      • 127.0.0.1是循环或本地主机地址:它总是指本地计算机。 从技术上讲,第一个八位字符串的任何地址作为回击地址是回击地址,但它是标准的。127``127.0.0.1

      (二)端口号

      一个计算机可能有多个服务器应用程序,客户端希望连接到,所以我们需要一种方法来将同一网络接口的流量定位到不同的进程。

      网络接口有多个由16位数识别的端口,端口0被保留,因此端口号码有效从1到65535。

      服务器进程与一个特定的端口结合,现在它正在听这个端口。 客户端必须知道服务器正在听的端口号码。 一些已知的端口为系统级进程保留,并为某些服务提供标准端口。

      • 终端22是标准的SSH端口,当您使用SSH连接时,软件会自动使用22端口。athena.dialup.mit.edu
      • 端口25是标准的电子邮件服务器端口。
      • 端口80是标准的Web服务器端口,当您在网页浏览器中连接到URL时,它将在端口80上连接到URL。http://web.mit.edu``18.9.22.69

      如果该端口不是标准端口,则将其指定为地址的一部分。http://128.2.39.10:9000``128.2.39.10

      当客户端连接到服务器时,传输的连接也使用客户端网络接口上的端口号码,通常是从可用的未知端口中随机选择的。

      (三)网络套接字

      一个接口代表一个客户端和服务器之间的连接.

      • 服务器进程使用一个听接口等待远程客户端的连接.
      • 连接的接口可以发送和接收到进程在连接的另一端的讯息。 它可以通过本地IP地址和端口号码以及远程地址和端口识别,允许服务器区分不同IP和不同远程端口相同的IP的同时连接。

      (四)缓冲区

      客户端和服务器通过网络交换以块形式发送数据。这些很少是单词大小的块,尽管它们可能是。发送者(发送请求的客户端或发送响应的服务器)通常写入一个大块(可能是整个字符串),如“HELLO,WORLD!”,它也可以包含20 MB的视频数据。

      网络将块分割成数据包,每个数据包通过网络进行单独的路由,而接收器则将数据包重新结合成字节流。

      结果是突然的数据传输。当你想读它们时,数据可能已经存在,或者你得等它们到达并重新组装。

      当数据到达时,它们输入一个缓冲器,这是存储数据的集合,直到您读取数据时才使用。

      (五)字节流

      输入或输出插座的数据是字节流.

      在爪哇,[InputStream对象代表输入程序的数据源,例如:

      • 使用 [FileInputStream从磁盘读取
      • [From the System.in]的用户输入
      • 从网络插座输入

      [OutputStream对象代表数据接收器,我们可以在此写数据。

      • [为保存到文件的文件输出流]
      • [系统输出)用于用户正常输出
      • [系统错误]输出
      • 输出到网络套接字

      (六)字符流

      我们可能需要将节点流动解释为Unicode字符流,因为Unicode可以代表多种人类语言(更不用说面部表情)。A是Unicode字符的序列,而不是字节序列,因此,如果我们想使用字符串来操作程序中的数据,所以我们需要把输入转换成Unicode,在编写时将Unicode返回节点。InputStream``OutputStream``String

      在 Java [中,读取器]和[写入器Unicode字符的输入和输出流。例如:

      • [FileReader]和 [FileWriter将文件看作字符序列而不是节点
      • 包装器 [InputStreamReader]和 [OutputStreamWriter向字符流推荐节点流

      一个I/O陷阱是确保程序使用正确的字符编码,这意味着一个字符串序列代表一个字符串序列的方式。最常见的Unicode字符编码是UTF-8。对于网络通信,UTF-8是正确的选择。通常,当您创建或 时,Java使用默认UTF-8编码。但是,当计算机上的其他程序使用不同的字符编码来读写文件时,就会出现问题,这意味着你的Java程序不能与它们交互。为了 纠正 这些 文件 的 相容性,在您的平台上,Java可能默认使用不同的字符编码,从系统设置中获取它-然后调试Java代码来执行网络通信,它的更好的默认值是UTF-8。例如,微软的Windows有一个非标准的编码,叫CP-1252,对于在Windows上运行的Java程序,这可能是默认设置。Reader``Writer

      字符编码错误可能很难检测.UTF-8、CP-1252和其他大多数字符编码都与ASCII的超级集相符,这是最古老的标准化字符编码之一。ASCII已经足够大了,可以表示英语,因此,英语文本往往不会受到字符编码错误的影响。但错误在于等待拉丁字母重复,或拉丁文以外的文字,或表情符号,甚至只是一个“花”的“弯曲”的口语。当字符编码有差异时,这些角色变成了垃圾。

      为了避免字符编码问题,请在构建或反对时明确指定字符编码。Reader``Writer

      (七)阻塞

      输入/输出电流显示阻塞行为。 例如,索克流:

      • 当输入插座缓冲区空时,直到数据可用为止,请调用该块。read
      • 当目标插座缓冲区填满时, 请在空格可用之前调用该块.write

      从程序员的角度来看,阻塞非常方便,因为程序员可以编写代码,好像电话总是有效。无论数据什么时候到达。如果缓冲器有数据(或空间),电话可能很快回来.但是,如果阅读或写作失败,则调用会阻塞。操作系统负责延迟线程的细节,直到它成功。read``write``write``read``write

      正如我们所看到的,在协调程序设计中,不仅仅在I/O中发生阻塞,协调模块并不像序列程序那样按锁步骤运行,所以当需要协调操作时,它们通常需要等待对方赶上。

      3.在Java中使用网络接口

      让我们看一下Java中的字符串编程的具体细节。 为了简便介绍,我们来看看一个简单的方法,它只显示客户端发送的所有内容,一个方法则从用户输入并发送给控制器EchoServer``EchoClient``EchoServer

      (一)客户端代码

      首先,我们将从客户的角度看它。 客户通过配置[Socket对象打开连接到主机名称和端口:

      如果服务器进程在指定的主机名上运行(在这个例子中,代表运行客户端进程的同一计算机)并听见连接到指定的端口,然后这个构造器将成功生成一个开放对象。如果服务器进程不收听端口,连接将失败并被触发。localhost``Socket``new Socket()``IOException

      假设连接成功,客户端现在可以访问两个字节流来与服务器进行通信:

      使用插座时,请记住输出是另一个进程的输入如果客户端和服务器有接口连接,则客户端有输出流流到服务器的输入流,反之亦然。

      客户端通常需要比简单的和接口更强大的操作。对于 ,我们希望使用上述和接口使用字符流而不是节点。我们还想读写整数字符,以换行符终止,这是一个提供和提供的功能。因此,我们将在提供这些操作的类中流 packing :InputStream``OutputStream``EchoClient``Reader``Writer``BufferedReader``PrintWriter

      注意明确的UTF-8字符编码,这是移动网络通信的最佳选择。

      基本循环是让用户在键盘上键入消息,发送到服务器,等待答复,并为服务器准备消息:EchoClient

      请注意,三种方法都是可行的被阻止。首先,客户端进程将阻塞,直到用户在 Konsole 上按某些内容并按 Enter 。然后,它将一起发送到服务器上,但是如果服务器的缓冲区是适当填满的,则此调用将阻塞,直到它能将消息置入缓冲区。然后,该进程将阻塞,直到服务器发送它的答复。println()``println()

      我们不应该感到惊讶,这个代码非常类似于用于执行与阻塞队列的讯息的代码。要向服务器发送请求,我们将请求写到插座的输出流中,就像我们在队列的例子中那样。为了接收回复,我们从输入流中读取,我们将使用 。发送和接收都是用来停止电话的。BlockingQueue.put()``BlockingQueue.take()

      上面的代码草图节隐藏了两个重要的细节。 首先,返回它正在读的当前是否在另一端关闭。...``readLine()``null``EchoClient

      其次,首先将消息放在对象内部的缓冲区中,即连接的客户端端口。 消息不会向服务器发送,除非缓冲区被填满或客户端关闭其连接。刷新缓冲器,迫使所有缓冲器内容被发送,是至关重要的:println()``PrintWriter

      最后,在退出循环后,我们关闭了流量和插座,这两个插座都将客户端完成信号发送到服务器,并释放了缓冲内存和其他与流量相关的资源:

      (二)服务器代码

      服务器从对象所代表的听接口开始。 服务器创建一个对象,以指定的端口数 listen 到来客户端连接:ServerSocket``ServerSocket

      如果另一个收听器插座已经在听这个端口,可能在另一个进程中,这个构造器将抛出一个,告诉您该地址已经使用。BindException

      注意,创建此套接头不需要一个主机名称,因为默认情况下,服务器套接头会对运行服务器进程的计算机的任何网络连接进行监听。

      与普通用户不同,它不提供任何字节流读写。 相反,它生成一系列新的客户端连接。Socket``ServerSocket``ServerSocket``Socket``accept()

      如果客户端连接不处于暂停状态,请在客户端到达后返回客户端连接对象。accept()``accept()``Socket

      一旦服务器连接到客户端,它将使用接口的输入和输出流的方式与客户端大致相同:从客户端接收消息,并准备和发送答复。

      服务器实现两个不同的方法来停止客户端与服务器之间的通信。在队列消息传输中,我们看到的一个方式是客户端发送鱼雷消息,在本例中。但客户可以停止的另一个方法是简单地关闭其连接的末端。确定此事件为索克输入流的末端,并通过返回发送信号。"quit"``readLine()``null

      (三)多线服务器代码

      我们编写的服务器代码只处理一次一个客户端,服务器周期是专为一个客户端,并且防止和重复读取和响应该客户端的消息,直到该客户端脱离连接。 只有这样,服务器才能从下一个客户端等待在队列中返回其连接。readFromClient.readLine()``ServerSocket``accept

      如果我们想使用封锁的I/O来同时处理多个客户端,服务器需要一个新的线程来处理每个新客户端的I/O。 当每个客户端特定的线程使用自己的客户端时,另一个线程(可能是主线程)是准备进行新的连接的。accept

      以下是多线性操作的原理:主线程运行的接入周期:EchoServer

      然后由每个新的连接创建的新线程运行客户端处理周期:

      (四)使用资源尝试关闭流和插座

      一种新的Java语法对于处理流和插座器特别有用: thetry-with-resources声明。 此句子自动调用其括号中声明的变量 pre-conduit:close()

      例如,以下描述了如何使用它来确保客户端的接口连接关闭:

      try-with-resources句子对于任何在使用后应该关闭的对象是有用的:

      • 字节流:InputStream``OutputStream
      • 字符流:Reader``Writer
      • 文件:FileInputStream``FileOutputStream``FileReader``FileWriter
      • 插座:Socket``ServerSocket

      Pythonwith语句具有类似的语义

      四、有线协议

      (1)远程登录客户端

      telnet是一个实用程序,允许您与收听器建立直接的网络连接并通过终端接口与它通信。 Windows、Linux和Mac OS X可以运行,虽然默认的新操作系统不安装它。

      首先,请在命令行上运行命令,检查是否安装了 telnet。如果您没有它,请查阅如何安装它([Linux],[Mac OS])。在Windows上,另一个电话网客户端是PuTTY,它有一个图形用户界面。telnet

      让我们看看一些有线协议的例子。

      (二)断续器

      1.多文本传输协议(HTTP)

      超文本传输协议(HTTP)是多维网络的语言,我们已经知道80端口是用于与Web服务器进行HTTP通信的众所周知的端口,所以让我们在命令行上与一个端口进行对话。

      通过以下命令尝试使用远程登录客户端。用户输入显示为绿色,为 telnet连接输入,替换行(按Enter)显示为.(如果您在Windows上使用PUTTY,您将在PuTTY的连接对话框中输入主机名称和端口,你还应该选择连接类型:原始,退出时关闭窗口: 永远.最后一个选项将防止服务器关闭其连接后立即消失的窗口。

      该命令获取网页。这是在网站上显示的页面的路径。因此,这个命令将获取该页的位置。因为80是HTTP的默认端口,这相当于在网页浏览器中浏览http://ww.eecs.Mit.edu/。结果是HTML代码显示的浏览器显示 EECS主页.GET``/``http://www.eecs.mit.edu:80/

      互联网协议由RFC标准定义(RFC代表"咨询",一些RFC最终被采纳为标准。[RFC 1945]定义HTTP版本1.0,它在[RFC 2616].1 中是 HTTP 1 。因此,对于许多网站,如果你想和他们谈谈,您可能需要使用HTTP 1.1.例如:

      这一次,您的请求必须以空行结束。 HTTP 版本 1.1 要求客户端在请求中指定一些额外的信息(称为标题),空行表示标题的结尾。

      您也可以发现,此请求发送后, telnet不会退出 - 这次,服务器将保持连接开放,以便您立即发送另一个请求. 手动退出 Telnet,键入翻译字符(可能 - ) 显示提示,然后键入:Ctrl``]``telnet>``quit

      2.简单邮件传输协议(SMTP)

      简单的邮件传输协议(SMTPs)是用于发送电子邮件的协议(不同的协议用于从接收箱中获取电子邮件的客户端程序)。由于电子邮件系统是在垃圾邮件出现之前设计的,因此,现代电子邮件通讯充满陷阱和启示方法,旨在防止滥用。但是我们仍然可以尝试使用SMTP。回想一下,已知的SMTP端口为25,这是MIT的电子邮件处理程序。mit-edu.mail.protection.outlook.com

      您需要在这里和这里填写您的IP地址填写您的用户名为了明确,它的意思是更改行。 这只适用于你在MITnet上,即使在那时,你的电子邮件可能被拒绝,因为它看起来很可疑:

      与HTTP相比,SMTP非常讲究,甚至包括用户可读的指令,告诉客户如何发送消息。

      (三)设计有线协议

      在设计连接协议时,同样的经验方法被应用于设计抽象数据类型:

      • 使不同的消息数目减少。 使用命令和响应的组合比许多复杂的消息更好。
      • 每个消息都应该有明确的目标和一致的行为。
      • 这个消息组必须足够让客户端发送他们需要发送的请求,并且服务器必须能够提供结果。

      正如我们要求代表独立于我们的类型一样,我们应该在协议中实现平台独立。HTTP可以在任何操作系统和任何网页浏览器上的任何网页服务器上使用。协议不指定如何将网页存储在磁盘上,服务器如何准备或生成网页,客户端将使用哪些算法来渲染它们, 等等.

      我们可以在这课中运用三个主要思想:

      • 免受错误侵害

        • 该协议应该为客户端和服务器生成和分析简单。 读写协议的简化代码(例如,自动从语法生成的解析器,或与一个匹配的文库的简单常用表达式)将减少错误的机会。

        • 考虑一个受损或恶意的客户端或服务器可能将垃圾数据填入协议中,以破坏在另一端的进程。

          垃圾邮件是一个例子:当我们上面说SMTP时,邮件服务器会问我们我们必须在SMTP上建立一个系统来阻止垃圾邮件的发送者直接撒谎。From:

          例如,允许客户端发送包含任意数量的数据请求的协议需要在服务器上小心处理,以避免没有足够的缓冲空间。

      • 易于理解:例如,选择基于文本的协议意味着我们可以通过阅读客户端/服务器交换的文本来调试通信错误。

      • 准备更改:例如,HTTP包含指定版本号的能力,以便客户端和服务器能够商定使用哪些版本的协议。 如果我们需要在将来更改协议,老客户或服务器可以继续工作,宣布他们将使用哪些版本。

      序列化(英语:Sequencing)是将内存的数据结构转换为易于存储或传输的格式的过程(与线程安全中的序列化不同)。同时,它发明了一种用于客户与服务器之间的数据序列化的新格式,而不是使用现有格式。例如,JSON(JavaScript对象表达式)是一个简单而广泛使用的格式,用于用字符串键序列基本值 、 集合和映射.

      (四)指定连线协议

      使用语法定义客户端和服务器的协议允许哪些消息。

      使用语法,我们可以看到前面的例子请求:

      • GET是的: 我们请服务器给我们一个页面。method
      • /about/是的: 我们想要什么样的描述?request-uri
      • HTTP/1.1是 .http-version
      • Host: web.mit.edu是某种标题,我们必须检查每个选项的规则,才能找出哪个选项。...-header
      • 我们可以看到为什么我们必须用空行结束请求:因为可能有多个标题以CRLF结束,所以我们最终会得到另一个CRLF。request``request
      • 我们没有任何东西,因为服务器不等我们发送一个,这可能只适用于其他类型的请求。message-body

      语法不够:在定义ADT时,它的作用与方法签名相似。 我们仍然需要以下标准:

      • 消息的先决条件是什么?

      例如,如果消息中的某个特定字段是数 string, 任何数值是否有效? 或者必须是服务器的已知记录ID? 在什么情况下可以发送消息? 有些消息仅在指定顺序发送时有效 吗?

      • 备份条件是什么? 基于邮件的服务器将执行哪些操作? 哪些服务器端数据将被更改? 服务器将向客户端发送哪些响应?

      5.测试客户端/服务器代码

      (一)从数据结构和算法中分离网络代码

      大多数客户端/服务器程序的ADT不需要依赖网络,确保它们被指定、测试和实现为独立的组件,它们无错误、易于理解和随时可更改,这部分是因为它们不涉及任何网络代码。

      如果需要从多个线程(例如,处理不同客户端连接的线程)并使用这些ADT,请尽量使用消息传输和线程安全队列,如有必要,您也可以使用[同步]或[限制、不变和现有的线安全数据类型的线安全策略]。

      (二)从流量代码中分开索克代码

      需要读写插座的函数或模块可能只需要访问输入/输出流,而不访问插座本身。 这种设计允许您通过连接它们到不来自插座的流上测试模块。

      两个有用的Java类是[ByteArrayInputStream]和[ByteArrayOutputStream假设我们测试这个方法:

      • 需要:

        input并且是开放的output

      • 影响:

        试着从它里读出一行,并试着写进它里一行input``output

      该方法通常与语法一起使用:

      如果基数转换是我们执行的函数,则应该单独指定、测试和执行。 但是现在我们还可以测试以下的读写行为:upperCaseLine

      为了分离和测试它,我们将将它通常依赖的组件(插座的输入/输出流)用符合相同的规格但具有固定行为的组件取代:固定输入的输入流和存储输出的输出流。inBytes``outBytes``upperCaseLine

      更复杂的模块测试策略可能使用模拟对象来模拟一个真正的客户端或服务器的行为,通过生成一个完整的固定交互序列,并否认从其他组件接收的每个消息的正确性。

      六、总结

      在客户端/服务器设计模式中,联接是不可避免的:多个客户端和多个服务器在网络上连接,同时发送和接收消息,并期望及时回复。当其他客户等待连接到慢客户或收到答复时,如果服务器停止等待客户端,它不会满足这些客户。同时,由于不同的客户同时修改共享变量数据,执行错误的计算或返回错误的结果的服务器不会满足任何人。

      当我们设计网络客户和服务器时,使我们的多线性代码免于错误,所有易于理解和准备改变的挑战都适用。这些进程同时运行(通常在不同的计算机上),任何想与多个客户端同时通信的服务器(或想与多个服务器通信的客户端)必须管理多线通信。

      本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/27736.html

          热门文章

          文章分类