做电子签章登录那个网站,建网站需要多大的宽带,做柜子好的设计网站,用织梦做网站视频本文主要介绍了TCP版本的回显服务器的编写。 一.TCP版本回显服务器
1.服务器 服务器的实现流程 1.接收请求并解析 2.根据请求计算出响应#xff08;业务流程#xff09; 3.把响应返回给客户端 代码#xff1a;
import java.io.IOException;
import java.io.InputStream;
i… 本文主要介绍了TCP版本的回显服务器的编写。 一.TCP版本回显服务器
1.服务器 服务器的实现流程 1.接收请求并解析 2.根据请求计算出响应业务流程 3.把响应返回给客户端 代码
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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** Tcp版本的回显服务器** 服务器*/public class TcpEchoServer {private ServerSocket serverSocketnull;//使用线程池此处不应该创建固定线程数目的线程池private ExecutorService service Executors.newCachedThreadPool();public TcpEchoServer(int port) throws IOException {serverSocketnew ServerSocket(port);}//这个操作会绑定端口public void start() throws IOException {System.out.println(服务器启动);while(true){//从内核中的连接获取到应用程序中/**** accept是把内核中已经建立好的连接给拿到应用程序中但是这里的返回值并非是* 一个connection对象而只是一个socket对象这个socket对象就像一个耳麦* 可以说话也可以听到对方的声音*/Socket clientSocketserverSocket.accept();//单个线程不方便完成这里的一边拉客一边介绍就需要多线程//多线程负责拉客//每次有一个新的客户端都创建一个新的线程去服务// Thread tnew Thread(()-{
// try {
// processConnection(clientSocket);
// } catch (IOException e) {
// e.printStackTrace();
// }
//
// });
// t.start();//使用线程池也可以解决service.submit(new Runnable() {Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}//通过这个方法来处理一个连接的逻辑private void processConnection(Socket clientSocket) throws IOException {System.out.printf([%s:%d]客户端上线 \n,clientSocket.getInetAddress().toString(),clientSocket.getPort());//接下来就可以读取请求根据请求计算响应返回响应三步走/*** socket对象内部包含了两个字节流对象可以把指责两个对象获取到* 完成后续的读写工作*/try(InputStream inputStreamclientSocket.getInputStream();OutputStream outputStreamclientSocket.getOutputStream()){while(true){//1.根据请求并解析为了读取方便直接使用scannerScanner scannernew Scanner(inputStream);if(!scanner.hasNext()){//读取完毕客户端下线System.out.printf([%s:%d]客户端下线 \n,clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}/***这里暗含了一个约定客户端发过来的请求* 得是文本数据同时还要包含空白符*/String requestscanner.next();//next一直读到空白符结束换行回车空格制表符等//2.根据请求计算响应String responseprocess(request);//3.把响应写给客户端/**用printWriter把outputstream包裹一下方便进行收发数据*/PrintWriter writernew PrintWriter(outputStream);/*** 使用printWriter的println方法把响应写给客户端结尾\n* 是为了方便客户端读取响应使用scanner.next读取*/writer.println(response);/*** 还需要加一个刷新缓冲区操作* io操作比较有开销相比于访问内存进行io次数越多程序的速度就越慢** 作为一块内存作为缓冲区写数据的时候先写到缓冲区里* 存一波数据统一进行io* printwriter内置了缓冲区* 手动刷新确保这里的数据是真的通过网卡发出去了而不是残留在缓冲区里** 加上flush是更稳妥的做法。*/writer.flush();//打印日志System.out.printf([%s:%d] rep:%s , resp:%s \n,clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,request);}} catch (IOException e) {e.printStackTrace();}finally {/*** socek有很多每来一个连接就会有一个连接*///在finally中加上close操作确保当前socket及时关闭。clientSocket.close();}}public String process(String request){return request;}public static void main(String[] args) throws IOException {TcpEchoServer servernew TcpEchoServer(9090);server.start();}}说明
1.循环之后服务器要做的事情不是读取客户端的请求而是先处理客户端的连接因为TCP是面向连接的。
2.一个服务器中要对应很对客户端服务器内核中有很多客户端连接。虽然内核中连接很多但是应用程序还是要一个一个的处理。
我们可以把内核中的连接看成 待办事项, 待办事项在队列中应用程序需要一个一个完成这些任务
要完成任务就要先取任务 ; 因此在处理请求之前要先通过accept从内核中获得请求。 我们可以把TCP连接的生成和获得连接的过程看作一个生产者消费者模型。 socket中会包含一个管理连接的队列这个队列是每个socket都有一份相互之间不会混淆。 3.当服务器执行到accept时此时如果客户端还没来accept就会阻塞直到有客户端连接成功为止。 accept是把内核中已经建立好的连接拿到应用程序中返回值是一个socket对象这个对象就像一个耳麦既可以说话也可以听到对反的声音。
也就是通过socket对象就可以和对方进行网络通信 此时这个回显服务器中涉及到两种socket 1.ServerSocket 相当于是在店外揽客的服务员揽到客人之后交给店内的服务员 2.clientSocket 店内负责招待的服务员 4.
scanner和printwriter没有close并不会导致文件资源暴露
流对象中持有的资源的两个部分
1内存对象销毁内存回收
2) 文件描述符 scanner和printwriter持有的是inputstream和outpustream的引用 5.服务器怎么感知到客户端下线的 hasNext在客户端没有发请求的时候也会阻塞一直阻塞到客户端发了请求或者是客户端退出它就返回了 2.客户端 基本实现流程 1.从控制台读取用户的输入 2.把输入的内容构造成请求发送给服务器 3.从服务器读取响应 4.把响应显示到控制台上 代码
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;/*** Tcp版本的服务器** 客户端*/public class TcpEchoClient {private Socket socketnull;//要和服务器通信就需要先知道服务器所在的位置public TcpEchoClient(String serverIp,int serverPort) throws IOException {//这个new操作就完成了tcp连接的建立socket new Socket(serverIp, serverPort);}private void start() {System.out.println(客户端启动);Scanner scannerConsolenew Scanner(System.in);try(InputStream inputStreamsocket.getInputStream();OutputStream outputStreamsocket.getOutputStream()){while(true){//1.从控制台输入字符串System.out.print(-);String requestscannerConsole.next();//2.把请求发送给服务器PrintWriter printWriternew PrintWriter(outputStream);printWriter.println(request);/*** 不要忘记flush* 确保数据真的发送出去了*/printWriter.flush();//3.从服务器读取响应Scanner scannerNetworknew Scanner(inputStream);String responsescannerNetwork.next();//4.把响应打印出来System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpEchoClient clientnew TcpEchoClient(127.0.0.1,9090);client.start();}}二.问题和解决方法
1.服务器问题
1.关闭当前的socket放在finally当中
客户端会有很多而每个客户端都有一个socket如果不关闭会消耗大量的资源。 2.(重点上面的代码是修改后的)
两个以上包含客户端发来的请求服务器无法正确地处理。
这是因为当第一个客户端来了accept会返回进入processConnection
在处理这个客户端请求过程中即使第二个客户端来了也无法第二次调用accept 解决办法改进成多线程 主线程负责accept和客户端建立连接 然后创建新的线程让新的线程去处理客户端的各种请求 更好的办法使用线程池 这样可以避免频繁创建和销毁线程。