定时器
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();
}
}
}