Java定时器和网络编程

定时器

java.util.Timer(定时器)

用来设定在什么时间去触发,可以绑定多个定时任务

TimerTask(定时器任务)

在该定时时间具体做的是什么,实现了Runnable接口,一个定时任务就是一个子线程

创建一个定时器

//创建一个定时器
Timer timer = new Timer();

在定时器绑定定时任务,延迟三秒后执行定时任务,定时任务使用TimerTask创建,该类是一个线程类,实现了Runnable接口,秩序重写run方法

timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("三秒后定时任务执行");
            }
        },3000);

定时器绑定一个延迟三秒执行,每隔1秒间隔执行一次

timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("会啦!");
            }
        },3000,1000);

定时器绑定一个在指定时间点执行的定时任务

Calendar c = Calendar.getInstance();
        c.set(2021,6,29,10,49);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("输出");
            }
        },c.getTime());

定时器绑定一个指定时间点执行,每隔一秒间隔执行一次

timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("2021年7月29");
            }
        },c.getTime(),1000);

取消绑定在定时器上的所有任务,如果要取消单个定时任务,则调用定时任务对象的cancel方法

timer.cancel();

Socket和IO

Socket端,通过Socket实现文件传输
实现网络通信,固定位置的服务器端,通信的为客户端
设置端口是开放的,必须先建立连接,通信是全双通的

基于Socket的java网络编程

Socket通讯过程

在这里插入图片描述

创建Socket对象

创建一个Socket对象,用来跟服务器端的Socket进行通信,传递数据,连接服务器的ip地址和端口号

创建客户端连接

//如果连接成功,返回本机的Socket对象
Socket s = new Socket("localhost", 8000);

连接成功之后,后续需要的流操作

//打开输入流,用来接受从服务器端发送过来的数据
InputStream is = s.getInputStream();
//打开输出流,用来向服务器端发送数据
OutputStream os = s.getOutputStream();
//封装输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//封装输出流
PrintWriter pw = new PrintWriter(os)

创建服务端连接

//供别人进行连接,提供一个端口来让客户端发现
//创建服务端端ServerSocket对象,指定端口接受客户端请求
ServerSocket server = new ServerSocket(8000);

//监听并连接及接受套接字:监听客户端发送的请求,该方法是阻塞的,
// 如果没有客户端发来请求一直是等待,直到客户端发来请求返回Socket对象
Socket socket = server.accept();

//获得客户端发出的输入流:读取从客户端发送过来的数据
BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//打开输出流对象:向客户端发送数据
PrintWriter os = new PrintWriter(socket.getOutputStream());
//将键盘输入放入缓冲流里面)
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in)))

练习

设置一个客户端,一个服务端,让他们相互进行通信

客户端类:Client

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

/**
 * 客户端Socket
 */
public class Client {

    public static void main(String[] args) {
        //创建一个Socket对象,用来跟服务器端的Socket进行通信,传递数据
        //连接服务器的ip地址和端口号
        try (
            //如果连接成功,返回本机的Socket对象
            Socket s = new Socket("localhost", 8000);
             //打开输入流,用来接受从服务器端发送过来的数据
             InputStream is = s.getInputStream();
             //打开输出流,用来向服务器端发送数据
             OutputStream os = s.getOutputStream();
             //封装输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            //封装输出流
            PrintWriter pw = new PrintWriter(os)) {

            Scanner sin = new Scanner(System.in);
            /**
             * 使用循环进行,只会一问一答
            //先读后写
            while (true) {

                //读取服务器发过来的数据
                System.out.println("服务器:" + br.readLine());
                //键盘输入
                String str = sin.nextLine();
                //向服务器发送数据
                pw.println(str);
                //刷新数据
                pw.flush();
            }*/

            /**
             * 使用线程来写,可以实现自由发送接受
             * 但是在主线中运行,不可以将流关闭,若是关闭了,就不能实现
             */
            new Thread(new SendMsgThread(s)).start();
            new Thread(new ReceiveMsgThread(s,"Server")).start();

            /*
            //先读再写
            //获取服务端的数据
            String line = br.readLine();
            //获取服务器发送过来的数据,直到读到数据才开始往下走
            System.out.println("服务端:" + line);

            //向服务器发送数据时,将输出流发送
            pw.println("我是客户端。");
            //刷新缓冲区,把数据及时传输到对方
            pw.flush();*/
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端类:Server

public class Server {

    public static void main(String[] args) {
        try (
             //供别人进行连接,提供一个端口来让客户端发现
             //创建服务端端ServerSocket对象,指定端口接受客户端请求
             ServerSocket server = new ServerSocket(8000);

             //监听并连接及接受套接字:监听客户端发送的请求,该方法是阻塞的,
             // 如果没有客户端发来请求一直是等待,直到客户端发来请求返回Socket对象
             Socket socket = server.accept();

             //获得客户端发出的输入流:读取从客户端发送过来的数据
             BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             //打开输出流对象:向客户端发送数据
             PrintWriter os = new PrintWriter(socket.getOutputStream());
             //将键盘输入放入缓冲流里面)
                BufferedReader sin = new BufferedReader(new InputStreamReader(System.in)))
             {
            System.out.println("客户端连接服务器成功");
            //服务端给客户端发送数据
//            os.println("欢迎你连接");
            //刷新缓冲区,把数据及时传输到对方
//            os.flush();

            //输出客户端消息
//            System.out.println("客户端:"+is.readLine());
            /**使用循环只能一句问,一句答
            //先写后读
            while (true) {
                //获得键盘输入
                String line = sin.readLine();
                os.println(line);
                os.flush();
                //接受客户端的套接字
                System.out.println("客户端:" + is.readLine());
//                System.out.println("服务端:" + line);
            }*/

         /**
          * 使用线程来实现
          * 但是在主线中运行,不可以将流关闭,若是关闭了,就不能实现
          */

         new Thread(new SendMsgThread(socket)).start();
         new Thread(new ReceiveMsgThread(socket,"Client")).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上述的会出现只能一问一答,不满足,所以应该使用多线程进行Socket的通信
客户端类Client

public class Client01 {
    public static void main(String[] args) {
        try
        {
            Socket s = new Socket("localhost",8100);
            //创建并开启发送消息线程
            new Thread(new SendMsgThread(s)).start();
            //创建并开启接受消息线程
            new Thread(new ReceiveMsgThread(s,"Server")).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务端类Server

public class Server01 {
    public static void main(String[] args) {
        try
        {
            ServerSocket ss = new ServerSocket(8100);
            Socket s = ss.accept();
            new Thread(new SendMsgThread(s)).start();
            new Thread(new ReceiveMsgThread(s,"Client")).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

接受消息:ReceiveMsgThread类

public class ReceiveMsgThread implements Runnable{

    /**
     * 接受信息的Socket
     */
    private Socket socket;
    private String name;

    public ReceiveMsgThread() {
    }

    public ReceiveMsgThread(Socket socket, String name) {
        this.socket = socket;
        this.name = name;
    }

    @Override
    public void run() {
        try(
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
            while (true) {
                System.out.println(name + "->"+ br.readLine());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

发送消息:SendMsgThread类

public class SendMsgThread implements Runnable{
    /**
     * 发送消息的Socket
     */
    private Socket s;
    public SendMsgThread(){}

    public SendMsgThread(Socket s){
        this.s = s;
    }

    @Override
    public void run() {
        try (
        PrintWriter pw = new PrintWriter(s.getOutputStream());)
        {
            Scanner scanner = new Scanner(System.in);
            while (true) {
            //将信息发送到接收端
                pw.println(scanner.nextLine());
                pw.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

TCP协议

是一种面向连接的可靠传输,在socket之间传输数据前先进行连接,中间是否被窃听就不能保证,传输格式是一样的,无差错的双向传输
应用场合
远程连接(TeInet)和文件传输(FTP)都需要不定长度的数据被可靠地传输
但是占用计算机的处理时间和网络贷款,因此TCP效率不如UDP

UDP协议

是一种无连接的不可靠的协议,发送发所发送的数据报并不一定以相同的次序到达接收方,将数据报可能以任何可能的路径传往目的地,因此能否到达目的地,到达时间以及内容正确性都不能被保证的,而且有大小限制,限定在64KB之内,
应用场合
局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些

Datagram

通讯图
发送和接收不需要建立连接

通讯流程

客户端发送包,服务端接收包

服务器端

创建数据套接字

//创建一个数据报套接字,绑定端口,用来接收或发送用户数据报
DatagramSocket s = new DatagramSocket(8000);

创建数据报,并且存包

byte[] b = new byte[1024];
//创建数据包,用来封装客户端发送过来的数据报,它是一个最终类,不能被重写
//指定接收数据的数组以及长度
DatagramPacket p = new DatagramPacket(b,b.length);
//接收数据存入数据包,如果没接收到,就会阻塞
s.receive(p);

获取数据包

//获取接收到的数据
byte[] data = p.getData();
//转换为字符串输出
System.out.println("客户端发送:" + new String(data,0,data.length));

Server类

public class Server {
    public static void main(String[] args) {
        //创建一个数据报套接字,绑定端口,用来接收或发送用户数据报
        try (DatagramSocket s = new DatagramSocket(8000)) {
            byte[] b = new byte[1024];
            //创建数据包,用来封装客户端发送过来的数据报,它是一个最终类,不能被重写
            //指定接收数据的数组以及长度
            DatagramPacket p = new DatagramPacket(b,b.length);

            //接收数据存入数据包,如果没接收到,就会阻塞
            s.receive(p);

            //获取接收到的数据
            byte[] data = p.getData();
            //转换为字符串输出
            System.out.println("客户端发送:" + new String(data,0,data.length));
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端

Client类

public class Client {
    public static void main(String[] args) {
        try (DatagramSocket ds = new DatagramSocket()) {
            String str = "ddddd";
            // 字符串 <-> 字节数组 getBytes        构造方法
            // 字符串 <-> 字符数组 toCharArray     构造方法
            // 字符串 <-> 数字   Xxx.parserXxx()     valuof
            //把字符串str封装到数据报中
            DatagramPacket pack = new DatagramPacket(str.getBytes(),str.getBytes().length, InetAddress.getByName("localhost"),8000);

            //发送数据包
            ds.send(pack);

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值