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

JavavEE中网络编程Socket套接字Ⅱ(TCP)

文章目录

  • TCP流套接字
    • ServerSocket API
    • Socket API
    • 🪁TcpEchoSever
    • 🪁TcpEchoClient
  • 总结

TCP流套接字

ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。
ServerSocket构造方法 :

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

ServerSocket 方法:

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待(接受客户端的连接)
void close()关闭此套接字

Socket accept() 既会给服务器用,也会给客户端用~

Socket API

Socket 构造方法:

方法签名方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

构造方法里面制定了IP和端口,构造方法就是在尝试和指定的服务器建立连接

Socket 方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址(对方的ip地址和端口)
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

通过 socket 可以获取到 两个流对象,分别用来读和写~~
TCP是面向字节流的,所以去读写 TCPsocket 本质上 和 读写文件 是一样的~

在这里插入图片描述

🪁TcpEchoSever

代码实现TCP 服务器:

package TCP;

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.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class TcpEchoSever {
    //代码中会设计多个 socket 对象,使用不同的名字来区分,使用listenSeverSocke来进行监听
    public ServerSocket listenSeverSocket = null;

    public TcpEchoSever(int port) throws IOException {
        listenSeverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService service = Executors.newCachedThreadPool();
        while (true) {
            //1. 先调用 appect 来 接受客户端的连接
            Socket clientSocket = listenSeverSocket.accept();
            //2.处理这个连接
            //使用线程池保证可以同时处理多个客户端
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnect(clientSocket);
                }
            });
        }



    }

    public void processConnect(Socket clientSocket) throws IOException {
        System.out.println("客户端上线!");
        //接下来处理客户端的请求
        //拿到输入流输出流对象
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            //客户端可能会发多条数据
            while(true) {
                //1.读取请求并解析
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()) {
                    //读完了,连接断开
                    System.out.printf("客户端读完了:[%s:%d]",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //读取到请求
                String request = scanner.next();
                //2. 根据请求计算响应
                String response = process(request);
                //3.把响应写回给客户端
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                //刷新缓冲区确保数据确实是通过网卡发送出去了
                printWriter.flush();

                //打印日志
                System.out.printf("[客户端ip地址: %s 客户端端口号: %d],请求: %s , 回应: %s \n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),request,response);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            clientSocket.close();
        }
    }
    public String process(String requst) {
        return requst;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoSever tcpEchoSever = new TcpEchoSever(9000);
        tcpEchoSever.start();
    }
}


listenSeverSocketclientSocket 的区别:
在这里插入图片描述
在这里插入图片描述

  • ❓为什么要使用多线程?

如果不使用多线程:
如果没有客户端建立连接,服务器就会阻塞到accept~
在这里插入图片描述

如果有一个客户端过来了,此时就会进入到 processConnection方法~
但是连接建立了,如果客户端还没发消息,此时代码就会阻塞在hasNext
在这里插入图片描述
就无法第二次调用到accept,也就无法处理第二个客户端了!!

  • ❓ 为什么要关闭 socket ? 而前面的 listenSocket 以及 udp 程序中的 socket 为什么就没有 close ?

在这里插入图片描述

socket也是一个文件
一个进程能够同时打开的文件个数,有上限!!! => PCB文件描述符表,不是无限的,要是满了就不能继续打开文件

listenSeverSocket 是在TCP服务器程序中只有唯一一个对象~ 就不会把文件描述符占满(随着进程的退出,自动释放)
在这里插入图片描述

clientSocket是在循环中,每个客户端连上来都要分配一个~
这个对象就会被反复的创建出实例,每创建一个,都要消耗一个 文件描述符~

因此就需要能够把不再使用的 clientSocket及时释放掉~

  • ❓为啥UDP没这个问题,而TCP有这个问题呢?

原因是TCP使用长连接这种连接方式~
UDP 客户端直接发消息即可~ 不必专注于处理某一个客户端~
TCP建立连接之后,要处理客户端的多次请求,才导致无法快速的调用到accept(长连接,导致专注于处理某一个客户端,务必要使用多线程)
如果TCP每个连接只处理一个客户端的请求,也能够保证快速调用到accept(短链接)

在这里插入图片描述

🪁TcpEchoClient

package TCP;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
    public Socket socket = null;

    public TcpEchoClient(String severIP,int severPort) throws IOException {
        //和服务器建立连接,需要知道服务器在哪
        socket = new Socket(severIP,severPort);
    }

    public void start() throws FileNotFoundException {
        Scanner scanner = new Scanner(System.in);
        try(OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream()) {
            while (true) {
                //1.从控制台读取数据,构造成一个请求
                System.out.print("->");
                String requestStr = scanner.next();
                //2.将请求发给服务器
                PrintWriter requestPrintWriter = new PrintWriter(outputStream);
                requestPrintWriter.println(requestStr);
                //如果不flush,可能导致请求没有真发出去
                requestPrintWriter.flush();
                //3.从服务器读取Tcp响应数据
                Scanner response = new Scanner(inputStream);
                String responseStr = response.next();
                //4.把响应显示到界面上
                System.out.println(responseStr);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9000);
        tcpEchoClient.start();
    }
}

  • ❓为什么Scanner 也不用关闭?

scannner这种关闭本质上是关闭了里面包含的InputStream,scanner自身是不会打开文件描述符的,而InputStream已经在try里面了,try运行完毕会自动关闭~

总结

在这里插入图片描述

你可以叫我哒哒呀
本篇到此结束
“莫愁千里路,自有到来风。”
我们顶峰相见!

相关文章:

  • Intel汇编-函数使用堆栈传递数据
  • 【Android程序开发】常用布局--线性布局LinearLayout
  • 基于Dijkstra、A*和动态规划的移动机器人路径规划(Matlab代码实现)
  • 国产EDA与FPGA芯片验证方案
  • 一种更优雅书写Python代码的方式
  • 自定义类型(结构体、位段、联合体、枚举)
  • 如何基于 GORM 实现 CreateOrUpdate 方法
  • Spring Boot核心之基本配置、日志配置、自动配置、条件注解
  • ArcGIS校园3D展示图制作详细教程
  • 【算法 | 实验6-1】n*n的网格,从左上角开始到右下角结束遍历所有的方块仅一次,总共有多少种不同的遍历路径
  • c++数据结构:图(邻接表)
  • SCOUT MINI Pro松灵机器人j建图定点步骤
  • 10个Pandas的小技巧
  • 广度优先遍历解决迷宫问题
  • 掌握 Microsoft Excel 宏和 Excel VBA
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • java2019面试题北京
  • Javascript基础之Array数组API
  • JAVA并发编程--1.基础概念
  • JDK9: 集成 Jshell 和 Maven 项目.
  • LeetCode算法系列_0891_子序列宽度之和
  • Magento 1.x 中文订单打印乱码
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • spring cloud gateway 源码解析(4)跨域问题处理
  • TypeScript实现数据结构(一)栈,队列,链表
  • Yii源码解读-服务定位器(Service Locator)
  • 编写高质量JavaScript代码之并发
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 服务器从安装到部署全过程(二)
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 入口文件开始,分析Vue源码实现
  • 项目实战-Api的解决方案
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • 新版博客前端前瞻
  • 译自由幺半群
  • 终端用户监控:真实用户监控还是模拟监控?
  • 《码出高效》学习笔记与书中错误记录
  • 从如何停掉 Promise 链说起
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • ​你们这样子,耽误我的工作进度怎么办?
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #android不同版本废弃api,新api。
  • #etcd#安装时出错
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (3)STL算法之搜索
  • (C语言)二分查找 超详细
  • (C语言)逆序输出字符串
  • (javascript)再说document.body.scrollTop的使用问题
  • (Spark3.2.0)Spark SQL 初探: 使用大数据分析2000万KF数据
  • (八)Flask之app.route装饰器函数的参数
  • (搬运以学习)flask 上下文的实现
  • (分类)KNN算法- 参数调优
  • (附源码)springboot“微印象”在线打印预约系统 毕业设计 061642