当前位置:   article > 正文

Java进阶14 TCP&日志&枚举

Java进阶14 TCP&日志&枚举

Java进阶14 TCP&日志&枚举

一、网络编程TCP

Java对基于TCP协议得网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

1、TCP协议发数据

1.1 构造方法
方法说明
Socket(InetAddress address,int port)创建流套接字并将其连接到指定IP指定端口号
Socket(String host,int port)创建流套接字并将其连接到指定主机上的指定端口号
1.2 相关方法
方法说明
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流
1.3 代码实现
  1. public class Client {
  2.    public static void main(String[] args) throws IOException {
  3.        //创建客户端的Socket对象(Socket)
  4.        //Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号
  5.        Socket s = new Socket("127.0.0.1",10000);
  6.        //获取输出流,写数据
  7.        //OutputStream getOutputStream() 返回此套接字的输出流
  8.        OutputStream os = s.getOutputStream();
  9.        os.write("hello,tcp,我来了".getBytes());
  10.        //释放资源
  11.        os.close();
  12.        s.close();
  13.   }
  14. }

2、TCP协议收数据

Java为客户端提供了Socket类,为服务端提供了ServeSocket类

2.1 构造方法
方法说明
ServerSocket(int port)创建绑定到指定端口的服务器套接字
2.2 相关方法
方法说明
Socket accept()监听要连接到此的套接字并接受它
2.3 代码实现
  1. public class Server {
  2.    public static void main(String[] args) throws IOException {
  3.        //创建服务器端的Socket对象(ServerSocket)
  4.        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字
  5.        ServerSocket ss = new ServerSocket(10000);
  6.        //Socket accept() 侦听要连接到此套接字并接受它
  7.        Socket s = ss.accept();
  8.        //获取输入流,读数据,并把数据显示在控制台
  9.        InputStream is = s.getInputStream();
  10.        byte[] bys = new byte[1024];
  11.        int len = is.read(bys);
  12.        String data = new String(bys,0,len);
  13.        System.out.println("数据是:" + data);
  14.        //释放资源
  15.        s.close();
  16.        ss.close();
  17.   }
  18. }

注意事项:

  • accept方法是阻塞的,作用就是等待客户端连接

  • 客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接

  • 针对客户端来讲,是往外写的,所以是输出流 针对服务器来讲,是往里读的,所以是输入流

  • read方法也是阻塞的

  • 客户端在关流的时候,还多了一个往服务器写结束标记的动作

  • 最后一步断开连接,通过四次挥手协议保证连接终止

3、三次握手和四次挥手

3.1 三次握手

3.2 四次挥手

4、TCP文件上传案例

  • 案例需求

    客户端:数据来自于本地文件,接收服务器反馈

    服务器:接收到的数据写入本地文件,给出反馈

  • 分析思路

    • 创建客户端对象,创建输入流对象指向文件,每读一次数据就给服务器输出一次数据,输出结束后使用shutdownOutput()方法告知服务端传输结束

    • 创建服务器对象,创建输出流对象指向文件,每接受一次数据就使用输出流输出到文件中,传输结束后,使用输出流给客户端反馈信息

    • 客户端接收服务端的回馈信息

  • 相关方法

    方法说明
    void shutdownInput()将此套接字的输入流放置在“流的末尾”
    void shutdownOutput()禁止用此套接字的输出流
  • 服务端优化思路

    • 服务端可以多次处理

      • 循环包裹while(true)

      • 使用UUID区分了重名文件,防止二次上传文件重名造成覆盖

    • 将服务器改进为多线程版本,满足多个客户端同时通信

    • 改为线程池

  • 代码实现

  1. public class Client {
  2.    public static void main(String[] args) throws IOException {
  3.        //1、创建Socket对象指定ip和端口
  4.        Socket socket = new Socket("192.168.11.251",8888);
  5.        //2、获取传输数据的流对象(网络流)
  6.        InputStream is = socket.getInputStream();
  7.        OutputStream os = socket.getOutputStream();
  8.        //3、将字节流转换为字符串(读写的内容有中文)
  9.        BufferedReader br = new BufferedReader(new InputStreamReader(is));
  10.        PrintStream ps = new PrintStream(os);
  11.        File file = new File("D:\\好哥哥.jpg");
  12.        //4、写出文件名
  13.        ps.println(file.getName());
  14.        //5、读取服务端消息
  15.        String state = br.readLine();
  16.        if("ok".equals(state)){
  17.            //6、上传文件(字节)
  18.            //创建本地字节输入流关联要上传的文件
  19.            FileInputStream fis = new FileInputStream(file);
  20.            byte[] bys = new byte[1024];
  21.            int len;
  22.            while((len=fis.read(bys))!=-1){
  23.                os.write(bys,0,len);
  24.           }
  25.            //重点:写出结束标记给服务端
  26.            socket.shutdownOutput();
  27.            fis.close();
  28.            //7、读取上传结果
  29.            String result = br.readLine();
  30.            System.out.println(result);
  31.            //8、关流
  32.            socket.close();
  33.       }
  34.   }
  35. }
  1. public class Server {
  2.    public static void main(String[] args) throws IOException {
  3.        //1、创建ServerSocket对象指定端口
  4.        ServerSocket server = new ServerSocket(8888);
  5.        System.out.println("服务端开启,等待客户端连接");
  6.        ThreadPoolExecutor pool = new ThreadPoolExecutor(
  7.                5,
  8.                10,
  9.                60,
  10.                TimeUnit.SECONDS,
  11.                new ArrayBlockingQueue<>(20),
  12.                Executors.defaultThreadFactory(),
  13.                new ThreadPoolExecutor.AbortPolicy()
  14.       );
  15.        while (true) {
  16.            //2、调用accept方法响应客户端的请求
  17.            Socket socket = server.accept();
  18.            //new Thread(new SubmitFileTask(socket)).start();
  19.            pool.submit(new FileSubmitTask(socket));
  20.       }
  21.   }
  22. }
  1. public class FileSubmitTask implements Runnable{
  2.    //为了拿到另一个类的数据,将其作为参数传进本类,在本类创建带参构造并接收该参数赋值给本类的成员变量,就拿到了
  3.    private Socket socket;
  4.    public FileSubmitTask(Socket socket) {
  5.        this.socket=socket;
  6.   }
  7.    @Override
  8.    public void run() {
  9.        try {
  10.            //3、获取传输的输入流对象(网络流)
  11.            InputStream is = socket.getInputStream();
  12.            OutputStream os = socket.getOutputStream();
  13.            //4、将字节流转换为字符串(读写的内容有中文)
  14.            BufferedReader br = new BufferedReader(new InputStreamReader(is));
  15.            PrintStream ps = new PrintStream(os);
  16.            //5、读取客户端发送的文件名
  17.            String fileName = br.readLine();
  18.            //关联服务端的存储路径
  19.            File update = new File("D:\\itheima\\Resource", UUID.randomUUID()+fileName);
  20.            //6、写给客户端消息
  21.            ps.println("ok");
  22.            //7、创建本地字节输出流,关联存储路径
  23.            FileOutputStream fos = new FileOutputStream(update);
  24.            //8、IO读写
  25.            byte[] bys = new byte[1024];
  26.            int len;
  27.            while((len = is.read(bys))!=-1){
  28.                fos.write(bys,0,len);
  29.           }
  30.            fos.close();
  31.            //9、写出上传成功
  32.            ps.println("上传成功!");
  33.            //10、关流
  34.            socket.close();
  35.       } catch (IOException e) {
  36.            e.printStackTrace();
  37.       }
  38.   }
  39. }

二、日志

程序中的日志可以用来记录程序运行过程中的信息,并可以进行永久存储。日志和输出语句的比较

输出语句日志技术
输出位置只能是控制台可以将日志信息写入到文件或者数据库中
取消日志需要修改代码,灵活性比较差不需要修改代码,灵活性比较好
多线程性能较差性能较好

1、日志体系结构

  • 日志规范:一些接口,提供给日志的实现框架设计的标准

  • 日志框架:牛人或者第三方公司已经做好的日志记录实现代码,后来直接可以拿去使用

  • 因为对Commons Logging 的接口不满意,有人就搞了SLF4J;因为对Log4j的性能不满意,有人就搞了Logback

2、Logback快速入门

  • 官网: Logback Home

  • 三个技术模块

    模块名介绍
    locback-core该模块为其他两个模块提供基础代码,必须有。
    logback-classic完整实现了slf4j API的模块。
    logback-accesslogback-access 模块与 Tomcat 和 Jetty 等 Servlet 容器集成,以提供 HTTP 访问日志功能
2.1 操纵步骤
第一步:引入jar包

jar包本质来说就是压缩包,内部存储的都是别人已经写好的代码。在需要导包的项目模块下,和src平级的位置,创建lib目录,将需要的jar包都放进去,然后勾选Add as Library。导jar包只是目前的做法,后面会学maven来管理jar包,不用手动导入

第二步:导入配置文件

将日志的配置信息文件(logback.xml)复制到模块的src

  • 日志级别和配置文件详解

    通过通过logback.xml 中的<appender>标签可以设置输出位置和日志信息的详细格式。通常可以设置2个日志输出位置:一个是控制台、一个是系统文件中

第三步:获取日志对象使用
  1. public class LogTest {
  2.    public static void main(String[] args) {
  3.        //获取记录日志对象
  4.        Logger logger = LoggerFactory.getLogger("LogTest.class");
  5.        //记录日志
  6.        logger.info("记录了一条日志");
  7.   }
  8. }

三、枚举

枚举是Java中的一种特殊类型,其作用是为了做信息的标志和信息的分类。枚举可以理解为一种多例设计模式,保证类的对象,在内存中只有固定的几个

1、定义格式

修饰符 enum 枚举类型{
    //第一行都是罗列枚举类实例的名称
    枚举项1,枚举项2,枚举项3;
}

  1. enum Season{
  2.    SPRING,SUMMER,AUTUMN,WINTER;
  3. }

2、枚举的特点

  • 所有枚举类都是 Enum 的子类

  • 我们可以通过"枚举类名.枚举项名称"去访问指定的枚举项

  • 每一个枚举项其实就是该枚举的一个对象

  • 枚举也是类, 可以定义成员变量

  • 枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略

  • 枚举类可以有构造器,但必须是 private 的,它默认的也是 private 的。枚举项的用法比较特殊:枚举("");

  • 枚举类也可以有抽象方法,但是枚举项必须重写该方法

3、示例代码

  1. public enum Season {
  2.    SPRING("春"){
  3.        //如果枚举类中有抽象方法
  4.        //那么在枚举项中必须要全部重写
  5.        @Override
  6.        public void show() {
  7.            System.out.println(this.name);
  8.       }
  9.   },
  10.    SUMMER("夏"){
  11.        @Override
  12.        public void show() {
  13.            System.out.println(this.name);
  14.       }
  15.   },
  16.    AUTUMN("秋"){
  17.        @Override
  18.        public void show() {
  19.            System.out.println(this.name);
  20.       }
  21.   },
  22.    WINTER("冬"){
  23.        @Override
  24.        public void show() {
  25.            System.out.println(this.name);
  26.       }
  27.   };
  28.    public String name;
  29.    //空参构造
  30.    //private Season(){}
  31.  
  32.    //有参构造
  33.    private Season(String name){
  34.        this.name = name;
  35.   }
  36.  
  37.    //抽象方法
  38.    public abstract void show();
  39. }
  40. public class EnumDemo {
  41.    public static void main(String[] args) {
  42.        //我们可以通过"枚举类名.枚举项名称"去访问指定的枚举项
  43.        System.out.println(Season.SPRING);
  44.        System.out.println(Season.SUMMER);
  45.        System.out.println(Season.AUTUMN);
  46.        System.out.println(Season.WINTER);
  47.        //每一个枚举项其实就是该枚举的一个对象
  48.        Season spring = Season.SPRING;
  49.   }
  50. }

4、做信息标志方式(2种)

4.1 常量

虽然可以实现可读性,但是入参不受约束,代码相对不够严谨

4.2 枚举

代码可读性好,入参约束严谨,代码优雅,是最好的信息分类技术!建议使用!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/629471
推荐阅读
相关标签
  

闽ICP备14008679号