公司网站注册要多少钱免费制作微网站

当前位置: 首页 > news >正文

公司网站注册要多少钱,免费制作微网站,云梦网站开发,小程序制作需要营业执照吗TCP流套接字编程 1. TCP #xff06; UDP 的区别 TCP 的核心特点是面向字节流#xff0c;读写数据的基本单位是字节 byte 2 API介绍 2.1 ServerSocket 定义 ServerSocket 是创建 TCP 服务端 Socket 的API。 构造方法 方法签名 方法说明 ServerS…       TCP流套接字编程     1. TCP UDP 的区别     TCP 的核心特点是面向字节流读写数据的基本单位是字节 byte  2 API介绍     2.1 ServerSocket     定义      ServerSocket 是创建 TCP 服务端 Socket 的API。 构造方法      方法签名  方法说明  ServerSocket(int port)创建一个服务端流套接字Socket并绑定到指定端口 所以服务器启动需要绑定端口号 方法       方法签名  方法说明   Socket accept()  开始监听指定端口(创建时绑定的端口)有客户端连接后返回一个服务端Socket对象并基于该Socket建立与客户端的连接否则阻塞等待   void close()   关闭此套接字 2.2  Socket     Socket 是客户端 Socket 或服务端中接收到客户端建立连接(accept方法)的请求后返回的服务端Socket。 不管是客户端还是服务端Socket都是双方建立连接以后保存的对端信息及用来与对方收发数据的。 构造方法      方法签名方法说明Socket(String host, int port)  创建一个客户端流套接字Socket并与对应IP的主机上对应端口的进程建立连接 这个方法的两个参数都是服务器的IP 端口这个版本的构造方法就是给客户端用的服务器怎么通过这个类来构造对象呢后续再来看 方法       方法签名方法说明InetAddress getlnetAddress()  返回套接字所连接的地址  InputStream getlnputStream()  返回此套接字的输入流  OutputStream getOutputStream()  返回此套接字的输出流 TCP没有 send()receive() 这样的操作但是 TCP 调用 getlnputStream() 会得到个 InputStream 对象调用 getOutputStream()得到一个 OutputStream 对象这两个对象是字节流对象 虽然 Socket 自身没有读写操作但是 Socket 可以拿到字节流对象就可以通过字节流对象来进行读写操作 3. 通过TCP实现回显服务器         TCP Echo Server     创建关联对象     通过构造方法绑定关联的端口号 和 UDP类似都是在构造对象的时候绑定端口号 实现 start()     处理客户端发送的连接     TCP 和 UDP 服务器 start() 的主循环的第一步有所区别 UDP 进入主循环就可以直接处理请求根据请求计算响应把响应返回客户端TCP服务器 进入主循环后因为 TCP 是有连接的所以第一步是先处理客户端发来的连接这个连接就类似于打电话在客户端打电话给服务器时服务器要先接通电话才可以进行后续的正常通信 所以TCP服务器进入主循环的第一步就是进行接通电话的操作而拨号操作是客户端来完成的 调用 ServerSocket 对象底下的 accept() 方法起到接听电话的作用 还需要接收 accept() 方法的返回值 如果客户端和服务器确实已经建立连接了那 accept() 是可以拿到这个请求连接的如果客户端没有发起连接那么 accept() 就会产生阻塞 和前面的 receive() 类似 TCP 服务器后续通过对clientSocket 进行读写数据来和客户端进行通信 进一步理解 ServerSocket 和 Socket 的职责划分     处理一个客户端的连接         处理连接的过程比较复杂因此我们把这个操作封装成一个方法 可能会涉及到多个客户端的请求和响应如果服务器接收到多个请求就要返还给客户端多个响应 服务器与客户端成功连接打印日志  这两个方法可以拿到对端客户端的 IP 端口号 获取输入流对象输出流对象      打印出客户端的IP端口号后就需要进一步地处理客户端的请求和响应需要借助 Socket 类内置的 InputStream OutputStream 来处理这些请求和响应 这里获取到的是输入流对象后续提供这个对象来读取客户端的请求 接下来获取输出流对象并且处理异常 拿到输入流对象输出流对象后后续读取请求就使用输入流对象返回响应就把响应的内容写入输出流对象中  接下来在 try 的代码块中实现读取请求和返回响应的操作这些操作分成三步 读取请求并解析 根据请求计算响应返回响应给客户端 因为在一次连接中这三个操作可能会解析多次所以我们提供 while 循环来处理 读取请求     下列操作读取到的请求是一个字节数组还需要手动把字节数组再转成字符串才方便后续的处理和打印 我们可以借助 Scanner 来进行更简单直接的读取操作既可以读请求读出来的请求又已经是一个字符串   把刚刚从 Socket 中拿到的 InputStream 填入 Scanner 中后续通过 Scanner 直接读取请求中的内容如果 Scanner 没有再读取到数据说明连接断开就可以结束循环了 所以读取请求可以直接借助 read()也可以借助 Scanner 来辅助完成 补充      Scanner 可以控制处理台输入又可以控制处理文件的输入还可以控制处理网络的输入Scanner 的构造方法 Scanner 的构造方法填入的是一个InputStream 对象 根据请求计算响应     当前编写的是一个回显服务器代码所以可以直接在计算响应的逻辑返回请求即可 返回响应给客户端     下列写法会直接拿到 response 中的字节数组然后通过 outputStream 提供的 write()来写入输出流对象即可 这种计算响应的方法是提供字节的方式填充输出流对象 除了上述写法我们还可以利用字符流的方式 这里的 writer 和 System.out 起到的的效果类似所以 printlnprintf 等等都可以通过调用 打印返回响应的日志和连接断开的日志     服务器一次连接可处理多个请求的原理    服务器 start() 的代码块中 process() 方法处理请求返回响应的逻辑相当于嵌套了两层 while 循环 因此可以在服务器与客户端的一次连接中服务器处理多个请求 如果在一次连接中客户端发送多次请求服务器就返还多个响应打一次电话可以说一句话或者很多句话 补充      一个连接一个请求短连接一个连接多个请求长连接因为连接过程的开销非常大所以在日常开发中更主流的是长连接一个连接处理多个请求就好比锁消除针对要加锁的多个逻辑每个逻辑都进行加锁开销非常大所以更科学的做法是把这些逻辑合在一起只进行一次加锁和领导汇报工作成果应该在一次电话中一次性汇总完毕而不是打多次电话。每次电话只汇报一个成果 TCP Echo Client     创建 Socket 对象     Socket 在客户端和服务器都可以使用服务器的 Socket 通过调用accept()拿到但是客户端的 Socket 就需要通过实例来创建对象 实现客户端构造方法     在客户端的构造方法中传入服务器的IP和端口号 传到构造方法中的字符串IP地址类似127.0.0.1这样的字符串不需要任何转换 对比UDP 的客户端TCP客户端在构造方法在实例Socket对象后就会在底层和对端建立TCP连接连接好后服务器会记录对端的信息实例化Socket对象时传入的IP和端口号 因此服务器的IP和端口号在TCP客户端中就不需要再创建变量来保存了 从控制台中读取请求发送给服务器     从控制台中读取用户输入信息作为请求     为了实现客户端能够和服务器在一次连接的情况下发送多次请求我们设置一个循环 这步操作可以读取刚刚输入控制台的一行信息读取到的信息作为客户端的请求 拿到输入流输出流     之后就把这个请求写入 Socket 对象中写的时候也需要拿到Socket对象的 InputStream 输入流 OutputStream 输出流 为了使用方便可以对拿到的输入流和输出流再套一层壳 完善循环逻辑      所以在主循环中第一步操作是从控制台中读取用户输入把读取到的输入设置为请求 第二步就是把请求发送给服务器 第三步就是读取服务器返回的响应并且把读取到的响应打印到控制台 客户端与服务器交互过程     区分客户端与服务器的 Socket 对象     下列服务器和客户端的两个 socket 对象分别在不同进程中甚至在不同主机中因此绝对不是同一个对象 这两个对象存在密切的关联关系可以把这两个 socket 对象理解为两部电话 接通这两部电话后从A听筒说话B可以听见从B听筒说话A可以听见从一边对Socket对象写数据另一边的 Socket 对象就可以读到但是这两个对象绝对不是同一部电话 处理细节问题     问题一冲刷缓冲区      完善 main 方法 程序运行结果 关掉客户端 再启动一次客户端并且发送一个数据并且一敲回车发现没有反应 为什么没有反应呢因为其实刚刚客户端代码并没有真的把请求发送出去 这个操作只是把数据放到 “发送缓冲区” 中还没有真正写入网卡里  发送缓冲区其实就是一块内存空间对网络/硬盘写数据是一个非常低效的操作如果频繁地调用这些比较低效的操作程序运行是非常缓慢的为了提高效率就引入一个内存缓冲区把要写入的数据都放入缓冲区中再统一进行发送这样可以减小写硬盘和写网络的次数但是提高效率的同时也会产生副作用就是调用 writer.println 这样的操作并没有真正地触发发送数据操作而只是把数据写入缓冲区当然把数据写入缓冲区而不是直接发送这样的行为是 PrintWriter的行为如果不套壳是可以直接发送的但是在实际开发中广泛使用了缓冲区这样的概念调用flush()来刷新缓冲区这个操作是非常关键的 如何真正地把数据发送出去呢我们要使用刷新操作调用 flush() 方法来冲刷缓冲区把缓冲区的数据强制写入 IO 设备中 客户端服务器交互结果 问题二针对 hasNext 对一个完整请求/响应设置标识符     println 的操作会自动加上一个 \n   但是如果在这个代码中不加这个 \n直接使用 print行不行呢 我们重新启动一下客户端并且发送内容发现客户端又没有反应了并且服务器也没有读到信息 造成上述原因是因为 next() 的问题修改成 print 后客户端输入的数据也是发送到服务器上了并且服务器也收到了但是服务器并没有真正处理因为服务器有一个hasNext()判断     补充     hasNext() 的行为是判断当前收到的数据是否包含“空白符”什么是空白符呢换行回车空格制表符翻页符……都是空白符遇到空白符hasNext()才会认为是一个完整的 next否则在遇到空白符之前hasNext() 都会阻塞。 所以刚刚在修改成 print 之后发送的内容是不包含空白符的内容hasNext() 就会阻塞而无法进入下面读取请求的逻辑     总结     使用 println是在约定一个请求/响应是在使用 \n 作为结束标记对端在读取数据的时候也会在读取到 \n 时判断读取到一个完整的请求/响应这是我们在使用TCP时特别需要注意的事项并且和UDP不一样UDP是以 DatagramPacket 作为单位的但是TCP则是以字节为单位但是实际上一个请求往往是以多个字节构成的到底多少个字节为一个完整的请求/响应就需要程序员想办法标记出来引入分割符是标记一次完整请求/响应的典型方式不一定是换行也可以是其他分割符  问题三根据不同Socket的生命周期判断是否需要手动关闭     在TCP服务器刚刚编写的代码中涉及两种Socket ServerSocket的生命周期贯彻整个服务区进程不需要手动关闭 clientSocket 的生命周期是一次连接而不是整个服务器进程 所以每个客户端连接都会创建一个新的 clientSocket每个客户端断开这个对象就应该 close() 了但是当前代码并没有对 clientSocket 进行 close() 释放没有在每次连接结束后对 clientSocket 进行关闭就会造成文件资源泄漏的问题文件一直在打开而不进行关闭在打开到一定程度会把文件描述表耗尽就无法继续打开新文件 在 poccessConnection() 方法的逻辑执行完毕之后我们就可以对 clientSocket 进行关闭     总结     对于是否需要手动关闭 Socket 需要我们分析请求它的生命周期是跟随整个进程还是跟随某个环节如果是跟随整个进程那么可以不手动关闭 Socket如果是每个请求都会创建应该 Socket或者每一次连接都会创建一个 Socket或者某一个环节的执行周期会创建一个Socket这样的情况就需要我们手动关闭 Socket 问题四服务器无法同时等待 accept() 等待已连接的客户端发送响应     一个服务器能同时给多个客户端提供服务那么刚刚编写的TCP服务器也可以处理多个请求吗我们关掉客户端再重新启动多个客户端     补充修改同时启动多个客户端的IDEA设置     IDEA 会默认只启动一个客户端再启动别的客户端会先关闭上一个启动的客户端我们通过这里的设置就可以让 IDEA 启动多个客户端 设置好后我们再来重新启动两个客户端 并且先后发送请求 结果在第二个客户端发送请求时又卡住了并且第一个创建的客户端多次发送请求的操作是没问题的服务器都会有一个正常响应但是第二个客户端无论怎么发送请求都不会有响应 并且我们通过服务器日志可以发现在第二个客户端上线时并没有再次打印日志 如果我们关掉第一个客户端第二个客户端发送的请求会马上被服务器接收并且返回响应 所以当前代码TCP服务器在同一时刻只能处理一个客户端发送的请求 针对上面出现的问题我们对服务器的处理请求返回响应这一块代码的关键逻辑进行分析 如果同一时刻有多个客户端对一个服务器进行连接那么第一个和服务器连接的客户端1 所以当前这个服务器代码如果已经在处理一个客户端的请求就没办法处理另一个客户端的请求服务器代码会卡在循环中无法重新调用 accept()连接新的服务器直到连接的客户端退出导致循环终止     总结     当前服务器代码无法同时等待 accept 和 等待用户请求在等待客户端发送请求的时候没办法等待 accept() 这个时候如果有新的客户端连接也无法接听电话一个专业销售没办法在给先来的顾客讲解产品的时候又去接待低级销售后面揽入店的顾客因此服务器的代码导致服务器一次只能处理一个客户端发送的请求这个代码是不合理的  4. 服务器引入多线程     如果只是单个线程无法同时响应多个客户端为了解决这个问题此处给每个客户端都分配一个线程 在服务器主线程中就只是进行 accept()每次有新的客户端 accept() 连接成功就创建一个新的线程又新线程负责完成后续对客户端 Socket 对象的引用 clientSocket 的读写操作 引入线程之后重新启动服务器和两个客户端可以发现服务器打印了两个带着不同端口号的日志 因此在引入线程之后服务器可以一边等待请求一边等待 accept() 连接新的客户端 5. 服务器引入线程池     客户端连接服务器就会创建新线程客户端断开连接客户端就会销毁线程为了避免频繁创建销毁线程也可以引入线程池 线程池 对于在服务器引入线程池一般不会使用 newFixedThreadPool因为会创建一个固定线程数的线程池意味着同时处理的客户端连接数目就固定了 把任务都交到线程池中线程池已经预先创建好了一些线程提前创建好的线程就可以立刻投入工作从而减少再去创建线程的开销 线程不是越多越好如果线程数量过多CPU的利用率无法再被提高还会导致系统调度速度下降并且创建线程也是需要系统资源的系统总体资源是有限的一个主机差不多只能创建几千个线程 无论是多线程还是线程池一个线程都对应一个客户端并且一个主机创建的线程数目是有上限的那如果有成千上万个客户端同时访问一个服务器一台主机该怎么办呢 IO多路复用/IO多路连接这里不重点讲解因为JVM中没有原生的 IO 多路复用 API而是把API重新封装已经不仅仅是多路复用了后续会详细讲解针对 Java 的 IO多路复用所使用的 NIO,Netty 等知名网络框架当前讲的IO是 BIO/Blocking IO 扩展     基于BIO(同步阻塞IO)的长连接会一直占用系统资源。对于并发要求很高的服务端系统来说这样的消耗是不能承受的。 由于每个连接都需要不停的阻塞等待接收数据所以每个连接都会在一个线程中运行。一次阻塞等待对应着一次请求、响应不停处理也就是长连接的特性:一直不关闭连接不停的处理请求。 实际应用时服务端一般是基于NIO(即同步非阻塞IO)来实现长连接性能可以极大的提升。  6. 长短连接     TCP发送数据时需要先建立连接什么时候关闭连接就决定是短连接还是长连接 长连接和短连接的概念     短连接每次接收到数据并返回响应后都关闭连接即是短连接。也就是说短连接只能一次收发数据。长连接不关闭连接一直保持连接状态双方不停的收发数据即是长连接。也就是说长连接可以多次收发数据。 长连接和短连接区别     建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接关闭连接;而长连接只需要第一次建立连接之后的请求、响应都可以直接传输。相对来说建立连接关闭连接也是要耗时的长连接效率更高。主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求也可以是服务端主动发。两者的使用场景有不同:短连接适用于客户端请求频率不高的场景如浏览网页等。长连接适用于客户端与服务端通信频繁的场景如聊天室实时游戏等。