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

网站建设 管理规范地方门户系统

网站建设 管理规范,地方门户系统,珠海市横琴建设局网站,做网站保定一)TCP Socket介绍: 1)TCP和UDP有着很大的不同#xff0c;TCP想要进行网络通信的话首先需要通信双方建立连接以后然后才可以进行通信#xff0c;TCP进行网络编程的方式和文件中的读写字节流类似#xff0c;是以字节为单位的流进行传输 2)针对于TCP的套接字来说#xff0c;J… 一)TCP Socket介绍: 1)TCP和UDP有着很大的不同TCP想要进行网络通信的话首先需要通信双方建立连接以后然后才可以进行通信TCP进行网络编程的方式和文件中的读写字节流类似是以字节为单位的流进行传输 2)针对于TCP的套接字来说JAVA本身提供了两个类来进行数据的传输一个是ServerSocket一个是Socket 2.1)ServerSocket:主要是给TCP服务器来进行使用的 2.2)Socket:我们既需要给客户端来进行使用也需要给服务器来进行使用 这样就是说TCP是不需要使用专门的类来进行表示传输的包因为TCP主要面向的是字节流我们是以字节为单位进行传输的 一)ServerSocket类的介绍: 二)Socket类的介绍: 第二个类是Socket这个类在客户端和服务器都会使用用来进行客户端和服务器之间的数据传输通信TCP的传输可以类似于打电话的场景客户端发送请求以后服务器调用ServerSocket类的accept()方法来建立连接这样在建立连接之后客户端和服务器之间就可以进行通信了Socket可以获取到文件也就是网卡的输入和输出流对象然后就可以通过流对象来针对于网卡进行读写了这就体现了TCP面向字节流全双工的特点  ServerSocket的实例是ListenSocketSocket的实例是clientSocket 如果有连接accept方法就会返回一个Socket对象也就是说进一步的客户端和服务器的交互就交给Socket了 1)也就是说咱们的ServerSocket就是一个接线员他并不会进行负责具体的办理业务而是把这个业务交给其他人来进行处理 2)每当ServerSocket通过accept方法感知到了有一个客户端来和我们的服务器建立连接了那么就会创建出一个Socket对象来进行通信服务器针对每一个客户端都会创建出一个Socket来进行通信 1)由于咱们的TCP是有连接的我们一进入到循环是不可以能开始读取数据而是需要先进行和客户端建立连接再进行传输数据先进行接电话而接电话的前提是有人给你打电话只有说在客户端Socket中通过构造方法指定服务器的IP地址和端口号这就相当于有人给你打电话而咱们的服务器里面的ServerSocket中的accept方法就会感知到并进行三次握手进行连接就是相当于是接电话 2)咱们的accept操作如果说此时没有客户端和你建立连接这个accept方法就会阻塞直到有人向我们当前的服务器建立了连接 3)因为客户端的请求的主机可能有多份所以服务器针对每一个客户端主机都会有一个Socket对象来进行处理 4)UDP的服务器进入主循环之后就尝试用receive读取请求了这是无连接的 5)但是我们的TCP是有连接的首先需要做的事先建立起连接相当于客户端的代码是货品就要通过一条公路来把这些货发送过去 6)当服务器运行的时候是否有客户端来建立连接不确定如果客户端没有建立连接accept就会进行阻塞等待也就是说咱们的TCP必须先通过accept方法先建立好连接才可以进行传输数据而我们的UDP完全不管3721就直接进行发送数据 1)在一开始服务器进行启动的时候我们就需要指定一个端口号后续客户端要根据这个端口来进行访问服务器的IP地址默认是主机IP 2)后续客户端进行访问服务器的时候目的IP就是服务器的IP不需要我们服务器的开发者来进行绑定了只要我们确定服务器在一台电脑IP地址就是确定了 三)服务器如何获取到客户端的IP地址和端口号 当服务器的ServerSocket通过accept()方法和客户端建立了链接那么此时客户端的信息就被保存到了Socket里面 1)可以直接通过ServerSocket实例调用accept方法返回的Socket对象的: getInetAddress()方法获取到IP地址 2)可以直接通过ServerSocket实例调用accept方法返回的Socket对象的getPort()方法就可以来进行获取到端口号 四)客户端和服务器的通信原理: 咱们这里面针对的TCPSocket的读写就和文件读写是一模一样的 1)在进行读Socket文件的数据就是相当于是在读取网络上面别的主机给咱们发送过来的数据 2)咱们在向Socket文件中写数据的时候就是相当于是在网络上面向目标主机发送数据 通过调用Socket对象里面的getInputStream()方法就可以进行获取到对应的流对象 全双工双向通信既可以读也是可以写的  五)服务器的读写过程: 1)再使用Socket对象clientSocket对象的getInputStream和getoutputStream对象得到一个字节流对象这时就可以进行读取和写入了此时的读操作可以调用inputstream.read()里面传入一个字节数组然后再转化成String但是比较麻烦判定结束也是比较麻烦的直到读到的结果是-1我们才会结束循环所以优先使用Scanner来进行读取这还是要多些循环使用inputStream.read()的时候就相当于是读取客户端发送过来的数据也就是在读取网卡 2)通过Socket对象的clientSocket的getInputStream()来进行获取到流对象但是具体读的时候Scanner 来进行具体的读new Scanner(InputStreaam)就巧妙地读到了客户端的请求,在调用scan.next()写的时候直接利用Printstream new Printstream()构造方法里面直接写Outputstream当调用println方法的时候就默认写回到客户端了 3)当我们使用outputStream.write()方法的时候就相当于向的客户端返回了数据 六)客户端读写文件的过程: 1)创建一个socket对象创建的时候同时指定服务器的IP和端口号然后把传入到socket中这个过程就会让客户端和服务器建立连接这就是三次握手这个过程就是内核完成的 2)这时客户端就可以通过Socket对象中的getInputStream和getOutputstream来得到一个字节流对象这时就可以与服务器进行通信了 3)在读的时候要注意此时只写一个Socket文件直接把服务器的端口号和IP地址传入进去进行构造就自动地和客户端进行了连接此时还要有InputStreamSocket和OutstreamSocket 七)关于关闭Socket文件 1)对于accept的返回值对应Socket这个文件是有一个close方法的如果打开一个文件后是要记得去关闭文件的如果不关闭就可能会造成文件资源泄漏的情况一个socket文件写完之后本应是要关闭的但是咱们前面写的UDP程序是不可以关闭的当时的socket是有生命周期的都是要跟随整个程序的当客户端不在工作时进程都没了PCB也没了文件描述符就更没有了 2)咱们的TCP服务器有一个listenSocket但是会有多个clientsocket可以说每一个客户端,每一个请求都对应一个clientSocket如果这个客户端断开链接了对应的clientSocket也就需要销毁了 1)TCP服务器的时候都针对了这里面的climentSocket(Socket创建的实例)关闭了一下但是我们对于listenSocket(ServerSocket创建的实例)却没有进行关闭直到服务器进行关闭 2)同时在UDP的代码里面也没有针对DatagramSocket对象和DatagramPacket来进行关闭 catch (IOException e) {e.printStackTrace();}finally {clientSocket.close();//listenSocket.close();在这里面是不能进行关闭的} 1)关闭的目的是为了释放资源释放资源的一个前提是这个资源已经不再进行使用了对于咱们的UDP的程序和ServerSocket来说这些Socket都是贯穿程序始终的只要程序启动运行我们就要用到什么时候咱们的服务器进程关闭什么时候不用但是咱们的服务器针对每一个客户端的Socket文件什么时候客户端断开链接了啥时候就不会再进行使用了 2)这些实例什么时候不用啥时候咱们的服务器进行关闭啥时候不用 3)咱们的这些资源最迟最迟也就会随着进程的退出一起进行释放了进程才是操作系统分配资源的最小单位那么这个进程曾经进行申请的资源也就没有了 4)但是咱们的clientSocket的生命周期是很短的针对咱们的每一个客户端程序都要进行创建一个climentSocket当对应的客户端断开连接之后咱们的服务器的对应的客户端的climentSocket对象也就永远不会再进行使用了就需要关闭文件释放资源咱们的climentSocket对象有很多每一个客户端都对应一个Socket对象就需要保证每一次进行处理完成的连接就必须进行释放 八)TCP服务器设计: 1)创建ServerSocket实例对象需要指定服务器的端口号 2)启动服务器使用accept方法和客户端建立连接如果没有客户端建立连接那么这里面的accept方法会阻塞 3)接受客户端的请求通过Socket对象的获取到InputStream来读取请求 4)除了客户端请求计算响应 5)将响应返回给客户端通过Socket获取到OutPutStream流对象来发送响应 package com.example.demo.Controller.Socket;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner;public class TcpServer {public ServerSocket listenSocketnull;public int serverPort;public TcpServer(int serverPort) throws IOException {this.serverPortserverPort;this.listenSocketnew ServerSocket(serverPort);}public void start() throws IOException {System.out.println(TCP服务器已经启动);while(true){//对于每一个客户端来说,服务器都会返回一个clientSocketSocket clientSocketlistenSocket.accept();//用来处理对应的客户端的请求procession(clientSocket);}}private void procession(Socket clientSocket) throws IOException {//此时使用了try catch这种写法,就不需要手动关闭inputStream和OutPutStream的资源了System.out.println(当前客户端和服务器建立了链接,IP地址和端口号是clientSocket.getInetAddress().getHostAddress()clientSocket.getPort());try(InputStream inputStreamclientSocket.getInputStream()){try( OutputStream outputStreamclientSocket.getOutputStream()) {Scanner scanner new Scanner(inputStream);PrintWriter writer new PrintWriter(outputStream);while (true) {if(!scanner.hasNext()){//当这个服务器收不到客户端的请求的时候,此时就会跳出循环,和客户端断开连接System.out.println(当前客户端断开连接clientSocket.getPort()clientSocket.getInetAddress());break;}//1.读取客户端的请求并解析String request scanner.next();//2.处理请求String response processMessage(request);//3.返回响应writer.println(response);writer.flush();}}}catch (IOException e){e.printStackTrace();}finally {clientSocket.close();}}private String processMessage(String request) {return request;}public static void main(String[] args) throws IOException {TcpServer servernew TcpServer(9090);server.start();} }1)这里面的hashNext方法会进行判断输入文件字符串键盘等输入流是否还存在着下一个输入如果有那么返回true如果没有那么直接返回falsehasNext本身会等待客户端那边的输入也会阻塞等待输入源的输入当客户端那一边关闭了链接输入源也就结束没有下一个数据说明读完了此时的hasNext就返回了false 2)next方法是一直读取到换行符/空格/其他空白符结束,但是最终返回结果里不包含空白符 3)这里面的hasNext()什么时候会返回false呢这是因为当客户端退出以后对应的流对象就读取到了EOF也就是文件结束标记那么这里面为啥会读到EOF呢这是因为客户端进程在进行退出的时候就会触发socket.close()也就是会触发FIN这个客户端关闭链接的请求也就是操作系统内核收到来自于客户端发送过来的FIN数据包那么就会将输入源结束标记成EOF 4)上面实现的TCP回显服务器的代码中有一个致命的缺陷就是 这个代码同一时间只能连接一个客户端, 也就是只能处理一个客户端的请求 public class TCPServer {ServerSocket listenSocketnull;public TCPServer(int serverPort) throws IOException {listenSocketnew ServerSocket(serverPort)}public void start() throws IOException {System.out.println(TCP服务器开始进行启动);while(true){Socket clientSocket listenSocket.accept();procession(clientSocket);}}private void procession(Socket clientSocket) throws IOException {System.out.printf(我们这一次客户端请求的IP地址是%s 端口号是%d,clientSocket.getInetAddress().toString(),clientSocket.getPort());try(InputStream inputStreamclientSocket.getInputStream()){try(OutputStream outputStream clientSocket.getOutputStream()){ //我们来进行循环处理请求,来进行处理响应,我们的一台主机是要给服务器发送多次请求的Scanner scannernew Scanner(inputStream);while(true){if(!scanner.hasNext()){ //客户端断开连接的代码System.out.printf(客户端断开连接%s %d,clientSocket.getInetAddress(),clientSocket.getPort());break;}} 我们在这里面使用Scanner是更方便的,如果说我们不使用Scanner就需要进行使用原生的inputStream中的read方法就可以了,只不过我们需要创建一个字节数组,然后使用stringbulider来进行拼接 // 1.读取请求并进行解析,读取Socket网卡String request scanner.next(); //2.根据请求计算并执行逻辑,我们创建process方法执行String responseprocess(request); //3.帮我们写的逻辑返回给客户端,为了方便起见,我们直接使用PrintWriter来进行对OutputStream来进行包裹一下PrintWriter printWriternew PrintWriter(outputStream);printWriter.println(response);printWriter.flush(); //4打印信息 System.out.printf([客户端的端口号是%d 客户端的IP地址是%s],请求数据是%s,响应数据是%s,clientSocket.getPort(),clientSocket.getInetAddress(),request,response);}} catch (IOException e) {e.printStackTrace();}finally {clientSocket.close();listenSocket.close();}}private String process(String request) {return request我爱你;}public static void main(String[] args) throws IOException {TCPServer servernew TCPServer(9099);server.start();} }九)TCP客户端设计: 1)创建Socket实例对象用于和服务器建立连接参数是服务器的IP地址和端口号在进行new Socket的过程中就会触发TCP三次握手 2)客户端启动, 用户输入请求构造构造请求并发送给服务器(使用OutputStream/PrintWriter), 要注意去刷新缓冲区保证数据成功写入网卡 3)读取服务器的响应并进行处理. package com.example.demo.Controller.Socket;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner;public class TcpClient {public Socket clientSocket;public String serverIP;public int serverPort;public TcpClient(String serverIP,int serverPort) throws IOException {this.serverPortserverPort;this.serverIPserverIP;this.clientSocketnew Socket(serverIP,serverPort);}public void start() throws IOException {System.out.println(客户端开始启动clientSocket.getPort()clientSocket.getInetAddress().getHostAddress());try(InputStream inputStreamclientSocket.getInputStream()) {try(OutputStream outputStreamclientSocket.getOutputStream()){Scanner scannernew Scanner(System.in);Scanner clientScannernew Scanner(inputStream);PrintWriter writernew PrintWriter(outputStream);while(true){//1.客户端输入命令System.out.println(客户端请输入命令---);String requestscanner.next();if(request.equals(exit)){System.out.println(客户端退出);return;}//2.客户端发送命令writer.println(request);writer.flush();//3.客户端接收响应String responseclientScanner.next();System.out.println(request);System.out.println(服务器返回的数据是response);}}} catch (IOException ex) {throw new RuntimeException(ex);}finally {clientSocket.close();}}public static void main(String[] args) throws IOException {TcpClient clientnew TcpClient(127.0.0.1,9090);client.start();} }import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner;class Request{int serverport0;String serverIpnull;Socket socketnull;public Request(String serverIp,int serverport)throws IOException {this.serverIpserverIp;this.serverportserverport;this.socketnew Socket(serverIp,serverport);}public void start()throws IOException { //1从键盘也就是控制台上读取请求Scanner scan new Scanner(System.in); try (InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {while (true) {System.out.println(请输入你的请求内容);System.out.println(-);String request scan.next();if (request.equals(goodbye)) {System.out.println(即将退出客户端);break;}//2把这个从键盘读取的内容,构造请求发给服务器PrintStream printStreamnew PrintStream(outputStream);printStream.println(request); //在这里我们怀疑println不是把数据发送到服务器中了而是放到缓冲区里面了我们刷新一下缓冲区强制进行发送printStream.flush();//如果不刷新,服务器无法及时收到数据//3我们从服务器那边读取响应,并进行解析Scanner scanreturnnew Scanner(inputStream);String responsescanreturn.next(); //4我们把结果显示在控制台上面String stringString.format(请求是 %s,回应是 %s,request,response);System.out.println(string);}}catch(IOException e){e.printStackTrace();}}public static void main(String[] args) throws IOException {//此处构建request对象的时候就相当于是和服务器建立连接Request requestnew Request(127.0.0.1,9090);request.start();} } 1)让Socket创建的同时就与服务器建立了链接相当于拨电话的操作这个操作对标于服务器中的climentSocket.accept接电话操作客户端的IP地址就是本机的IP地址端口号是由系统自动进行分配的 2)在这里面传入的IP和端口号的含义表示的是不是自己进行绑定而是表示和这个IP端口建立连接  咱们的服务器要想拿到客户端的端口号就要通过climentSocket(Socket创建的实例) IP地址:climentSocket.getInetAddress().toString() 端口号:climentSocket.getPort() 要使用printWriter中的println方法而不是write() 1)针对写操作要进行刷新缓冲区如果没有这个刷新客户端时不能第一时间获取到这个响应结果 2)对于每一次请求都对应着一个Socket都要创建一个procession方法来进行处理接下来就来处理请求和响应 3)这里面的针对TCP Socket的文件读写是和文件读写是一模一样的socket文件来进行读和写TCP和UDP是全双工既可以读Socket文件也可以写Socket文件 十)如何进行判断是否断开连接建立连接 1.1)咱们的服务器会在进行处理每一个客户端的请求的时候会进行判定if(scanner.hasNext)判断我们的客户端是否已经读取完成了如果我们的客户端断开连接那么我们的if判定就会返回服务器读取就会完毕 1.2)如果客户端已经连接了那么ServerSocket的accept()方法就会返回如果客户端断开连接了那么我们的服务器的hasNext()方法就会感知到  十一)基于TCP的网络计算机: 进行运算要吃CPU进行传输吃的是网络宽带 客户端给服务器发送的请求: 字符串:第一个操作数第二个操作数运算符 服务器给客户端返回的响应:计算结果 下面是实现运算服务器的代码 所谓的自定义协议一定是在开发之前就约定好的开发过程中就要让客户端和服务器都能够严格遵守协议约定好的格式直接使用文本分隔符假设传输的请求和响应中各自有几十个字段况且有些字段是可选的 package demo2;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner;public class TcpRequestRequest {public String serverIP;public int serverPort;private Socket socketnull;public TcpRequestRequest(String serverIP, int serverPort) throws IOException {this.serverIPserverIP;this.serverPortserverPort;this.socketnew Socket(serverIP,serverPort);}public void start(){System.out.println(客户端开始进行启动);try(InputStream inputStreamsocket.getInputStream()) {try (OutputStream outputStream socket.getOutputStream()) {while(true){ //1.先进行输入数据Scanner scannernew Scanner(System.in);System.out.println(请输入第一个操作数);String s1scanner.nextLine();System.out.println(请输入操作符);String s2scanner.nextLine();System.out.println(请输入第二个操作数);String s3scanner.nextLine();String requests1Bs2Bs3; //2.发送数据//2.1再进行发送数据PrintStream printStream new PrintStream(outputStream);printStream.println(request);printStream.flush();//2.2再进行接受服务器的响应Scanner result new Scanner(inputStream);String response result.nextLine();//2.3打印在控制台上面System.out.println(response);System.out.println(是否接下来要继续进行计算?);String next scanner.nextLine();if (next.equals(N)) {System.out.println(即将退出客户端);break;}} } } catch (IOException e) {e.printStackTrace(); }}public static void main(String[] args) throws IOException {TcpRequestRequest requestnew TcpRequestRequest(127.0.0.1,9999);request.start();} }package demo2;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner;public class TcpClimentServer {public int serverPort;private ServerSocket listensocketnull;public TcpClimentServer(int serverPort) throws IOException {this.serverPortserverPort;this.listensocketnew ServerSocket(serverPort);}public void start() throws IOException {System.out.println(开始启动我们的服务器);while(true){Socket socketlistensocket.accept();System.out.printf(客户端的IP地址是%s,客户端的端口号是%d,socket.getInetAddress(),socket.getPort());procession(socket);}}private void procession(Socket socket) {try(InputStream inputStreamsocket.getInputStream()) {try(OutputStream outputStreamsocket.getOutputStream()){while(true){ //1.我们先进行读取客户端的请求Scanner scannernew Scanner(inputStream);String requestscanner.nextLine(); //2.根据请求进行处理业务逻辑String responseProcess(request); //3.根据业务逻辑来进行计算响应PrintWriter writernew PrintWriter(outputStream);writer.println(response);writer.flush(); //4.显示计算结果System.out.printf(客户端的请求是%s 服务器返回的响应是%s,request,response);}} catch (IOException e) {e.printStackTrace();}} catch (IOException e) {e.printStackTrace();}}public String Process(String request){//1.判断数据是否为空if(requestnull||request.equals()){return 当前传输数据有误,什么也没有传输过来;}//2.按照标点符号来进行分割String[] stringsrequest.split(B);if(strings.length!3){return 您当前数据所进行输入的格式是存在错误的;}//3.取出里面的数字和符号来进行运算int result0;Integer s1Integer.parseInt(strings[0]);Integer s2Integer.parseInt(strings[2]);String operatestrings[1];if(operate.equals()){results1s2;}else if(operate.equals(-)){results1-s2;}else if(operate.equals(*)){results1*s2;}else{results1/s2;}return String.valueOf(result);}public static void main(String[] args) throws IOException {TcpClimentServer servernew TcpClimentServer(9999);server.start();} }十二)当前TCP服务器同一时刻只能进行处理一个客户端的请求:  现象:一个服务器对应多个客户端此时就需要多次启动这个客户端实例 现象是当我们启动第一个客户端之后服务器进行提示上线当我们启动第二个客户端的时候服务器此时就没有任何响应了况且发送请求的时候没有进行任何响应但是当我们退出客户端的时候此时神奇的事情出现了服务器提示了客户端二上线况且客户端二也得到了服务器的响应但是此时客户端三没有任何反应当我们把客户端2的主机退出那么客户端3给服务器发送的数据就有效了所以当前的服务器在同一时刻只可以给一个客户端提供服务只有前一个客户端下了下一个客户端才可以上来 原因:第一个客户端尝试与服务器建立连接的时候服务器会与客户端建立连接这个时候客户端发送数据服务器就会做出响应客户端多次发送数据服务器就会循环处理请求 3.1)调用listenSocket的accept方法与客户端建立连接 3.2)执行process方法来循环进行处理客户端给服务器发送过来的请求除非说第一个客户端断开链接了否则就无法进行处理其他请求了因为它陷在我们的一个procession方法里面的while(sc.hashNext())方法无法出来了 3.3)第二个第三个第四个客户端想要给服务器发送数据不可能成功建立连接 1)原因是在服务器中的hasNext那里在等待第一个客户端发送数据服务器本身并没有退出第一个客户端对应的这个procession这个方法也就是说直接死在第一个客户端的procession这个方法的while循环里面(一直进行工作)所以整个服务器的程序就卡死在hasNext这个代码块里面了这样就导致主函数的外层的while循环无法进行下一轮也就无法重新进行循环调用第二次accept方法服务器无法再次调用accept方法与下一个客户端进行三次握手建立连接 2)最终造成的结果是客户端什么时候退出hasNext()方法就进行返回procession()方法就进行返回第一次外层循环结束进行第二层外层循环才有可能继续调用accept()方法 3)所以问题的关键在于如果第一个客户端没有退出此时服务器的逻辑就一直在procession里面打转也就没有机会进行外层循环再次调用accept方法也就无法再次去处理第二个连接第一个客户端退出以后结束里面的循环结束上一个procession服务器才可以执行到第二个accept才可以建立连接 3)这个问题就类似于好像你接了一个电话和对方你一言我一语的进行通话别人再继续给我们进行打电话我们就没有办法进行接通了 4)咱们解决上述问题就需要第一次执行的procession方法不能影响到咱们的下一次循环扫描accept的执行   1)咱们的accept方法调用一次就接通一个如果说多的调用几次我们就可以多接通几个所以解决方法就是说:咱们的调用procession方法和accept方法执行的调用不会相互干扰 2)也就是说不能让咱们的procession方法里面的循环影响到前面accept方法的执行 3)怎么样才可以说让我们的procession方法自己去执行自己的并且让这个accept执行自己的呢让我们的accept被反复调用到又让我们的procession来进行反复地进行处理客户端请求呢 1)引入多线程之后保证主线程始终在调用accept每次都有一个新的连接来创建新线程来处理请求响应线程都是一个独立的执行流每一个线程都会执行自己的同一段逻辑并发执行  2)咱们调用accept方法的线程和调用procession方法的线程是互不干扰的呀 让主线程主线程专门负责进行accept和客户端建立连接, 每收到一个连接, 创建新的线程, 由新线程来负责处理这个新的客户端请求 3)使用线程池相较于多线程是更优化的方案, 使用多线程的话每连接到一个客户端就会就会创建一个线程, 如果同一时刻连接过多, 这里线程创建和销毁的开销就比较大了, 使用线程池就可以减少这里开销, 提高效率 public class Server {HashMapString,String mapnew HashMap();int serverport0;ServerSocket listenSocketnull;public Server(int serverport) throws IOException {this.serverportserverport;this.listenSocketnew ServerSocket(serverport);map.put(及时雨,宋江);map.put(国民女神,高圆圆);map.put(老子,李佳伟);}public void start() throws IOException {System.out.println(服务器即将启动);while(true){Socket climentSocketlistenSocket.accept(); //我们的改进方案是每一次accept方法成功,那么我们就创建一个新的线程,有新的线程负责来执行这次process方法,这样就实现了代码之间的解耦合Thread threadnew Thread(){public void run(){String strString.format(客户端的IP地址是 %s 客户端对应的端口号是 %d,climentSocket.getInetAddress().toString(),climentSocket.getPort());System.out.println(str);try {procession(climentSocket);} catch (IOException e) {e.printStackTrace();}}};thread.start();}}public void procession(Socket climentSocket) throws IOException {try(InputStream inputStreamclimentSocket.getInputStream();OutputStream outputStreamclimentSocket.getOutputStream()){Scanner scannernew Scanner(inputStream);PrintStream printStreamnew PrintStream(outputStream);while(true){if(!scanner.hasNext()){System.out.println(这个客户端对应的服务器已经完成了工作);return;}String requestscanner.next();String responsehhhh(request);printStream.println(response);String strString.format(请求是 %s 响应是 %s,request,response);System.out.println(str);}}}public String hhhh(String request){return map.getOrDefault(request,没有这个参数);} 这是改进后的服务器代码 import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class Server {HashMapString,String mapnew HashMap();int serverport0;ServerSocket listenSocketnull;public Server(int serverport) throws IOException {this.serverportserverport;this.listenSocketnew ServerSocket(serverport);map.put(及时雨,宋江);map.put(国民女神,高圆圆);map.put(老子,李佳伟);}public void start() throws IOException {System.out.println(服务器即将启动);while(true){Socket climentSocketlistenSocket.accept();String strString.format(客户端的IP地址是 %s 客户端对应的端口号是 %d,climentSocket.getInetAddress().toString(),climentSocket.getPort());System.out.println(str);ExecutorService executorService Executors.newCachedThreadPool();executorService.submit(new Runnable() {Overridepublic void run() {try {procession(climentSocket);} catch (IOException e) {e.printStackTrace();}}});}}public void procession(Socket climentSocket) throws IOException {try(InputStream inputStreamclimentSocket.getInputStream();OutputStream outputStreamclimentSocket.getOutputStream()){Scanner scannernew Scanner(inputStream);PrintStream printStreamnew PrintStream(outputStream);while(true){ if(!scanner.hasNext()){System.out.printf(客户端程序 %s,客户端的端口号是%d,断开连接,climentSocket.getInetAddress(),climentSocket.getPort());return;}String requestscanner.next();String responsehhhh(request);printStream.println(response);String strString.format(请求是 %s 响应是 %s,request,response);System.out.println(str);}}}public String hhhh(String request){return map.getOrDefault(request,没有这个参数);}public static void main(String[] args) throws IOException {Server servernew Server(8080);server.start();} }1)这样子咱们的主线程第一次循环之后调用accept方法就会立刻创建线程立即调用start方法接下来就会又会执行start方法中的循环 2)主要就是说线程就是一个独立的执行流线程之间是并发执行的在我们的start方法在进行执行accept方法进行返回的时候立即就会创建新线程同时本次循环结束在新线程里面我们就会不断地接受此次客户端的请求 1)咱们刚才的UDP版本的程序就没有用到多线程因为咱们的UDP编程不需要处理连接咱们的UDP只需要一个循环就可以处理所有客户端的请求 2)DatagramPacket requestpacketnew DatagramPacket(new Byte[4096],4096); requestSocket.receive(requestPacket)这个start方法进入循环不管谁不管哪一个客户端发送过来了请求服务器都会进行处理返回一个循环把所有客户端都给伺候好了 3)但是咱们的TCP既要处理连接又要处理一个连接中的若干次请求就需要两个循环里层循环就会影响外层循环的进度了况且两个循环是在两个不同的方法里面 4)主线程循环调用accept方法当我们有客户端尝试进行连接的时候直接让主线程创建一个新线程由多个新线程负责并发处理若干个客户端的请求在新线程里面我们通过while循环来进行处理请求这个时候多线程就是并发执行的关系了就是各自执行各自的彼此之间不会相互干扰 5)在主循环里面会循环的调用accept方法在每一个新线程里面会进行循环的读取客户端的请求这样就做到了把两个循环拆分成了两个不同的线程宏观上面两个线程各自去执行各自的就是一个并发执行的关系因此也就不会相互影响的 6)但是在实际开发中客户端的数目可能有很多每一个客户端进行连接都要分配一个线程 虽然线程比进程更轻量但是如果有很多的客户端连接又退出这就会导致咱们当前服务器频繁的创建销毁线程系统的开销和系统的负荷量是很大的那么我们如何改进这个问题这时我们就需要用到线程池了 7)当客户端new Socket()成功的时候其实本质上从操作系统内核层面已经建立好了连接(TCP三次握手)但是咱们的应用程序没有接通这个链接应用程序没有进行这个接听动作也没有办法真的去说话 8)改成多线程版本之后虽然咱们procession的代码已经进入到处理连接的阶段了但是并不会影响去调用accept方法当前的这个问题就是会说其实是电话打过去了但是对方没有进行接听尝试进行建立连接的请求已经发送过去了对方也知道了也就是说咱们的操作系统内核已经把工作做好了但是我们的上层应用程序还是不想去接触 假设极端情况下一个服务器面临着很多很多客户端这些客户端连接上了并没有退出这个时候服务器这边就会存在很多很多线程会有上万个线程这个情况下会有一些其他的问题吗这是科学的解决方法吗 1)实际上这种现象是不安全的不科学每一个线程都会占据一定的系统资源如果线程太多太多了这时候许多系统资源(内存资源CPU资源)就会十分紧张达到一定程度机器可能就会宕机因为你创建线程就要有PCB还要为这个线程分配栈和程序计数器的空间 2)上万个线程会抢这一两个CPU操作系统的内核就会十分激烈线程之间的调度开销是十分激烈的比如说线程1正在执行执行一会被操作系统的内核调度出去了下一次啥时候上CPU执行就不知道了因为排的队实在是太多太多了线程之间非常卷服务器对于客户端的响应能力返回数据的时间就会大大降低了 3)例如在双十一/春运这种场景如果一个系统同时收到太高的并发就可能会出现问题例如每一个并发都需要消耗系统资源并发多了系统资源消耗就多了系统剩下的资源就少了响应能力就变慢了再进一步把系统都给消耗没了系统也就无法正常工作了(我们的服务器不可能响应无数个客户端) 1)使用携程来进行代替线程完成并发很多协程的实现是一个M:N的关系(一大堆的携程是通过一个线程来进行完成的)协程比线程还要轻量 2)使用IO多路复用的机制完成并发; 2.1)这会从根本上来解决服务器高并发的这样一个问题在内核里面来支持这样的功能 2.2)假设现在有1W个客户端在这个服务器里面就会用一定的数据结构把1W个客户端对应的Socket保存好不需要一个线程对应一个客户端一共就有几个线程IO多路复用机制就可以做到哪个Socket上面有数据了就通知到这个应用程序让这个线程从这个socket里面来读数据虽然是1w个客户端但是在同一时刻也就只有不到1K个客户端来给服务器发送请求靠系统来通知应用程序谁可以读就去读谁通过一个线程就可以处理多个Socket在C方向典型实现就是epoll(mac/linux)kqueue(windows)我们在Java里面通过NIO这样的一系列的库封装了epoll等IO多路复用机制 3)使用多个服务器(分布式)就算我们使用协程就算使用IO多路复用咱们的系统同一时刻处理的线程数仍然是有限的只是节省了一个客户端对应的资源但是随着客户端的增加还是会消耗更多的资源我们就使用更多的硬件资源此时每一台主机承受的压力就小了 以上三种都是一个基本处理高并发场景的所使用的方法
http://www.dnsts.com.cn/news/111432.html

相关文章:

  • 何为门户网站深圳骏域网站建设专家
  • 做网站代管理三年扬州市住房和建设局网站
  • 网站运营团队各岗位的职责是什么做网站前段用什么软件
  • 百度站长工具怎么推广网站汕头百姓网二手房出售
  • 全景网站开发摄影设计素材
  • 南宁商城网站推广公司网站设计的导航栏怎么做
  • 南京市住房建设网站discuz建网站
  • 便民工具 网站开发网站界面设计案例
  • 在线做高中试卷的网站政务服务中心 网站建设
  • 行业网站开发中国菲律宾铁路项目
  • 甘肃手机版建站系统哪个好餐饮网站建设案例
  • 想做机械加工和橡胶生意怎么做网站忻州企业网站建设
  • 红酒购物网站源码网站建设需要考哪些证
  • 国外搜索网站建设高新区建网站外包
  • 网站图片做伪静态广告设计是做什么的
  • 富阳公司做网站深圳网站设计九曲网站建设
  • 思源黑体做网站东莞做网站优化天助网络
  • 罗平县建设局网站深圳网站建设有免费的吗
  • 上海网站开发建设服务画册设计价格
  • 网站开发用户需求合肥装饰公司
  • 甘肃省嘉峪关建设局网站在淘宝做网站可以改域名吗
  • 有没有catia做幕墙的网站绿园区建设局网站
  • 没有公司怎么做网站友情链接交换网站
  • 西安室内设计公司排名流程优化
  • 北京通网站建设价格网络营销有哪些内容
  • wordpress站点统计小工具seo做的比较好的网站的几个特征
  • led网站建设哪家专业wordpress数据表优化
  • 公益网站设计网络营销试卷及答案
  • 网页设计教育培训哪里好百度搜索seo
  • 杯子电子商务网站的建设怎样才能建设网站