在百度做橱柜网站,外文网站建设完成,车间管理系统,模板wordpress演示站怎么做文章目录一、ServerSocket API构造方法常用方法二、Socket API构造方法常用方法注意事项三、TCP中的长短连接E1:一发一收#xff08;短连接#xff09;E2:请求响应#xff08;短连接#xff09;E3#xff1a;多线程下的TCP回响服务器说明#xff1a;这部分说实话有点懵短连接E2:请求响应短连接E3多线程下的TCP回响服务器说明这部分说实话有点懵理解上有点吃力这里暂时先放到这有新的认识再进行回来修改。 一、ServerSocket API
它是创建TCP服务端Socket的api
构造方法
方法签名说明ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口
给服务器绑定端口。
常用方法
方法签名说明Socket accept()开始监听创建时绑定的端口有客户端连接后返回一个服务端Socket 对象并基于该Socket建立与客户端的连接否则阻塞等待void close()关闭此套接字
二、Socket API
即会给客户端和服务器使用。
构造方法
方法签名说明Socket(String host, int port)创建一个客户端流套接字Socket并与对应IP的主机上对应端口的 进程建立连接
常用方法
方法签名说明InetAddress getInetAddress()返回套接字所连接的地址InputStream getInputStream()返回此套接字的输入流OutputStream getOutputStream()返回此套接字的输出流
TCP中socket对象对于服务器而言是靠accpet返回的对于客户端而言是靠代码内部构造的。
注意事项 TCP 中的ClientSocket的socket对象需要释放而前边Server对象和UDP的都没释放为什么这里需要呢原因有二 1.这里的socket声明周期比较短UDP里边的和TCP服务器里的是要跟随整个程序的。 2.这里的socket对象可能比较多可能会把文件描述符表占满。 outputStream相当于一个文件描述符一个socket文件通过这个对象就可以往这个文件描述符中写数据。 OutStream自身方法不方便写字符串把流进行转换一下用一个PW对象来表示对应的文件还是通过一个。 不过使用PW打印流写往一个地方写只不过的写的更方便了。 println是写往控制台上往网卡上…… 三、TCP中的长短连接
客户端socket对象构造,会触发tcp建立连接 短连接每次接收到数据并返回响应后都关闭连接即是短连接。也就是说短连接只能一次收发数据。 长连接不关闭连接一直保持连接状态双方不停的收发数据即是长连接。也就是说长连接可以多次收发数据。 两者区别如下 建立连接、关闭连接的耗时短连接每次请求、响应都需要建立连接关闭连接而长连接只需要第一次建立连接之后的请求、响应都可以直接传输。相对来说建立连接关闭连接也是要耗时的长连接效率更高。 主动发送请求不同短连接一般是客户端主动向服务端发送请求而长连接可以是客户端主动发送请求也可以是服务端主动发。 两者的使用场景有不同短连接适用于客户端请求频率不高的场景如浏览网页等。长连接适用于客户端与服务端通信频繁的场景如聊天室实时游戏等。 E1:一发一收短连接
服务器端代码、客户端代码
public class Code04_TCPEchoServer {private ServerSocket serverSocketnull;//这种可以返回Socket类型的对象public Code04_TCPEchoServer(int port) throws IOException {serverSocketnew ServerSocket(port);}public void start() throws IOException {System.out.println(启动服务器成功);while(true){Socket clientSocketserverSocket.accept();//效果是接收连接//前提是客户端来建立连接若有则连接若无则阻塞等待//建立连接processConnection(clientSocket);}}//使用这个方法来处理一个连接//这个连接对应到一个客户端但是这里可能会设计到多次交互private void processConnection(Socket clientSocket) {System.out.printf([%s:%d] 客户端上线!,clientSocket.getInetAddress().toString(),clientSocket.getPort());//基于上述socket进行通信try(InputStream inputStreamclientSocket.getInputStream();//由于要处理多个请求所以也是使用循环来进行OutputStream outputStreamclientSocket.getOutputStream()){while(true){//1.读取请求Scanner scannernew Scanner(inputStream);if(!scanner.hasNext()){
// System.out.println(当前连接已关闭);System.out.printf([%s:%d] 客户端下线!,clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}System.out.println();String requestscanner.next();//next读到换行符/其他空白符结束但是不包含//2.根据请求构造响应String responseprocess(request);//3.返回响应结果PrintWriter printWriter new PrintWriter(outputStream);printWriter.println(response);// 此处加上 flush 保证数据确实发送出去了.printWriter.flush();//4.打印中间结果System.out.printf(response);}} catch (IOException e) {e.printStackTrace();}finally {try{clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {Code04_TCPEchoServer tcpEchoServernew Code04_TCPEchoServer(1200);tcpEchoServer.start();}
}public class Code05_TCPEchoClient {private Socket socket null;//这是用来接收服务器的socket对象public Code05_TCPEchoClient(String serverIp, int serverPort) throws IOException {// Socket 构造方法, 能够识别 点分十进制格式的 IP 地址. 比 DatagramPacket 更方便.// new 这个对象的同时, 就会进行 TCP 连接操作.socket new Socket(serverIp, serverPort);}public void start() {System.out.println(客户端启动!);Scanner scanner new Scanner(System.in);try (InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {while (true) {// 1. 先从键盘上读取用户输入的内容System.out.print( );String request scanner.next();if (request.equals(exit)) {System.out.println(bye);break;}// 2. 把读到的内容构造成请求, 发送给服务器.PrintWriter printWriter new PrintWriter(outputStream);printWriter.println(request);// 此处加上 flush 保证数据确实发送出去了.printWriter.flush();// 3. 读取服务器的响应Scanner respScanner new Scanner(inputStream);String response respScanner.next();// 4. 把响应内容显示到界面上System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {Code05_TCPEchoClient client new Code05_TCPEchoClient(127.0.0.1, 1200);client.start();}
}这里不用println写是不行的原因如下 TCP协议是面向字节流的协议。接收方一次读多少个字节需要我们在数据传输中进行明确约定。 这里的next和println是相互制约的。next在等请求中的结束符。 enter是换行符但是这里按下enter是把next里内容送上去并没有把换行符读到。 E2:请求响应短连接
略
与UDP类似这里是在服务器处加上了相关的业务逻辑。
E3多线程下的TCP回响服务器
public class Code04_TCPEchoServer {private ServerSocket serverSocketnull;//这种可以返回Socket类型的对象public Code04_TCPEchoServer(int port) throws IOException {serverSocketnew ServerSocket(port);}public void start() throws IOException {System.out.println(启动服务器成功);ExecutorService threadPool Executors.newCachedThreadPool();while(true){Socket clientSocketserverSocket.accept();//效果是接收连接//前提是客户端来建立连接若有则连接若无则阻塞等待//建立连接threadPool.submit(()-{processConnection(clientSocket);});}}//使用这个方法来处理一个连接//这个连接对应到一个客户端但是这里可能会设计到多次交互private void processConnection(Socket clientSocket) {System.out.printf([%s:%d] 客户端上线!,clientSocket.getInetAddress().toString(),clientSocket.getPort());//基于上述socket进行通信try(InputStream inputStreamclientSocket.getInputStream();//由于要处理多个请求所以也是使用循环来进行OutputStream outputStreamclientSocket.getOutputStream()){while(true){//1.读取请求Scanner scannernew Scanner(inputStream);if(!scanner.hasNext()){
// System.out.println(当前连接已关闭);System.out.printf([%s:%d] 客户端下线!,clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}System.out.println();String requestscanner.next();//next读到换行符/其他空白符结束但是不包含//2.根据请求构造响应String responseprocess(request);//3.返回响应结果PrintWriter printWriter new PrintWriter(outputStream);printWriter.println(response);// 此处加上 flush 保证数据确实发送出去了.printWriter.flush();//4.打印中间结果System.out.printf(response);}} catch (IOException e) {e.printStackTrace();}finally {try{clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {Code04_TCPEchoServer tcpEchoServernew Code04_TCPEchoServer(1200);tcpEchoServer.start();}
}1.修改允许多个客户端其中之后服务器只显示一个客户端上线 2.客户端1发送消息可以客户端2发送消息没有响应【占线】 一旦客户端1一下线客户端2立即上线。 为什么这里需要我们结合服务器的启动代码分析 这里是多线程的问题。其实还是有点不太懂但具体是哪里说不上来 当有客户端连上服务器后代码就执行到了processConnection这个方法里的while循环这时另一个客户端再次尝试发送请求由于此时调到这里循环不结束processConnection方法就结束不了进一步也就无法再次accept了 解决办法使用多线程。 主线程。专门负责accpet每次收到一个连接创建新线程由这个新线程负责这个新的客户端 这里因为有可能频繁的申请释放线程所以这里我们采用的是线程池。 一般的版本 Thread tnew Thread(()-{processConnection(clientSocket);
});
t.start();说明虽然线程池的加入会一定程度上解决多个客户端的需要同时启动的效率问题。但是线程的创建与销毁始终还是比较耗时间的。一旦客户端的数量激增接近阈值还是存在的问题的。 对此操作系统提供了io多路复用的机制缓解。 【基于BIO同步阻塞IO的长连接会一直占用系统资源。对于并发要求很高的服务端系统来说这样的消耗是不能承受的。 实际应用时服务端一般是基于NIO即同步非阻塞IO来实现长连接性能可以极大的提升。】