等待唤醒机制,UDP通信和TCP通信
Posted lzw123-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了等待唤醒机制,UDP通信和TCP通信相关的知识,希望对你有一定的参考价值。
等待唤醒机制
通过等待唤醒机制使各个线程能有效的利用资源。
等待唤醒机制所涉及到的方法:
wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。
所谓唤醒的意思就是让 线程池中的线程具备执行资格。
必须注意的是:这些方法都是在 同步中才有效;
同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪 个锁上的线程。而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。
例子:
代码如下:
模拟资源类:
package com.oracle.Resourse; public class Resourse { public String name; public String sex; public boolean flag = false; }
输入线程任务类:
package com.oracle.Resourse; public class InputR implements Runnable{ private int i=0; private Resourse r=new Resourse(); public InputR(Resourse r){ this.r=r; } public void run() { while(true){ synchronized (r){ if(r.flag){ try { r.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(i%2==0){ r.name="张三"; r.sex="男"; }else{ r.name="LISI"; r.sex="NV"; } r.flag=true; r.notify(); } i++; } } }
输出线程任务类:
package com.oracle.Resourse; public class OutputR implements Runnable{ private Resourse r=new Resourse(); public OutputR(Resourse r){ this.r=r; } public void run() { while(true){ synchronized (r){ if(r.flag==false){ try { r.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(r.name+"..."+r.sex); r.flag=false; r.notify(); } } } }
测试类:
package com.oracle.Resourse; public class TEST { public static void main(String[] args) { Resourse r=new Resourse(); InputR in=new InputR(r); OutputR out=new OutputR(r) ; Thread t1=new Thread(in); Thread t2=new Thread(out); t1.start(); t2.start(); } }
结果如下:
InetAddress类
InetAdderss类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法
例子:
public class Example01 { public static void main(String[] args) throws Exception { InetAddress local = InetAddress.getLocalHost(); InetAddress remote = InetAddress.getByName("www.oracle.cn"); System.out.println("本机的IP地址:" + local.getHostAddress()); System.out.println("oracle的IP地址:" + remote.getHostAddress()); System.out.println("oracle的主机名为:" + remote.getHostName()); } }
UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。
DatagramPacket类
该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。
构造方法
发送端一定要明确指出数据的目的地(ip地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。
DatagramPacket类中的常用方法
DatagramSocket类
DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包
构造方法:
该构造方法用于创建发送端的DatagramSocket对象,在创建DatagramSocket对象时,并没有指定端口号,此时,系统会分配一个没有被其它网络程序所使用的端口号。
该构造方法既可用于创建接收端的DatagramSocket对象,又可以创建发送端的DatagramSocket对象,在创建接收端的DatagramSocket对象时,必须要指定一个端口号,这样就可以监听指定的端口。
常用方法:
例子:
UDP完成数据的发送:
package com.oracle.UdpTcp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; public class UDPSend { public static void main(String[] args) throws IOException { Scanner sc=new Scanner(System.in ); //InetAddress类调用getbyName方法返回一个本类对象 InetAddress i=InetAddress.getByName("192.168.1.171"); //创建DatagramSocket对象, DatagramSocket ds = new DatagramSocket(); while(true){ //构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 //定义字节数组来接收发送端的内容 String mes=sc.next(); byte[] bytes=mes.getBytes(); //装包 DatagramPacket dp = new DatagramPacket(bytes, bytes.length, i, 6666); ds.send(dp); } } }
UDP完成数据的接收:
package com.oracle.UdpTcp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; public class UdpS { public static void main(String[] args) throws IOException { //1,创建DatagramSocket对象,并指定端口号 DatagramSocket ds = new DatagramSocket(8888); //创建接收数据的字节数组 byte[] bytes =new byte[1024]; //创建接收数据包 while(true){ DatagramPacket dp = new DatagramPacket(bytes, bytes.length); //接收 ds.receive(dp); //获取数据 String ip=dp.getAddress().getHostAddress();//获取IP地址 int port=dp.getPort();//获取端口号 int length = dp.getLength();//获取多少条数据 System.out.println(ip+"..."+port+"发送的内容为"+new String(bytes,0,length)); } // ds.close(); } }
TCP通信
在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。
通信时,首先创建代表服务器端的ServerSocket对象,该对象相当于开启一个服务,并等待客户端的连接,然后创建代表客户端的Socket对象向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信。
ServerSocket类
构造方法
使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上(参数port就是端口号)。
ServerSocket的常用方法:
ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。
Socket类
构造方法
常用方法;
方法声明 |
功能描述 |
int getPort() |
该方法返回一个int类型对象,该对象是Socket对象与服务器端连接的端口号 |
InetAddress getLocalAddress() |
该方法用于获取Socket对象绑定的本地IP地址,并将IP地址封装成InetAddress类型的对象返回 |
void close() |
该方法用于关闭Socket连接,结束本次通信。在关闭socket之前,应将与socket相关的所有的输入/输出流全部关闭,这是因为一个良好的程序应该在执行完毕时释放所有的资源 |
InputStream getInputStream() |
该方法返回一个InputStream类型的输入流对象,如果该对象是由服务器端的Socket返回,就用于读取客户端发送的数据,反之,用于读取服务器端发送的数据 |
OutputStream getOutputStream() |
该方法返回一个OutputStream类型的输出流对象,如果该对象是由服务器端的Socket返回,就用于向客户端发送数据,反之,用于向服务器端发送数据 |
例子:
客户端程序:
package com.oracle.Tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class TCPK { public static void main(String[] args) throws IOException { //创建socket对象,连接服务器 Socket s=new Socket ("127.0.0.1",7777 ); //通过客户端套接字对象Socket对象中的获取字节输出流的方法 OutputStream out=s.getOutputStream(); //将数据写向服务器 out.write("服务器你好".getBytes()); //接收服务器端的回复 InputStream in=s.getInputStream(); byte[] bytes=new byte[1024]; int len=in.read(bytes); System.out.println(new String(bytes,0,len)); s.close(); } }
服务器端程序:
package com.oracle.Tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPS { public static void main(String[] args) throws IOException { //创建服务器套接字 ServerSocket ss=new ServerSocket(7777); //调用ACCEPT方法与客户端创建链接 Socket s=ss.accept(); InputStream in=s.getInputStream(); byte[] bytes=new byte[1024]; int len=in.read(bytes); System.out.println(new String(bytes,0,len)); //服务器端给回复 OutputStream out=s.getOutputStream(); out.write("收到".getBytes()); } }
文件上传案例
首先编写服务器端程序,用来接收图片。
package com.oracle.TCPS; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; public class TCPS { public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(7500);//明确端口号 Socket socket=server.accept(); //读取文件,明确数据源 InputStream in=socket.getInputStream(); //明确目的地 File file=new File("e:\\JPG"); //判断文件是否存在 if(!file.exists()){ file.mkdirs();//没有则创建文件夹 } //域名+毫秒值+6位随机数 // 随机数 String num=""; for(int i=0;i<6;i++){ num+=new Random().nextInt(10); } String filename="oracle"+System.currentTimeMillis()+num+".jpg"; FileOutputStream fos=new FileOutputStream(file+File.separator+filename); //复制‘ int len=0; byte[] bytes=new byte[1024]; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len);//往服务器端写 } //回复客户端 OutputStream out=socket.getOutputStream(); out.write("上传成功".getBytes()); fos.close(); server.close(); } }
客户端程序:
package com.oracle.TCPS; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class TCPK { public static void main(String[] args) throws UnknownHostException, IOException { //创建socket对象,连接服务器 Socket socket=new Socket ("127.0.0.1",7500 ); //获取Socket流中的输出流,功能:用来把数据写到服务器 OutputStream out=socket.getOutputStream(); //从文件读到客户端 FileInputStream fis=new FileInputStream("E:\\QQwenjian\\1972680739\\FileRecv\\0601.jpg"); //定义字节数组接收文件 byte[] bytes=new byte[1024]; int len=0; while((len=fis.read(bytes))!=-1){ out.write(bytes,0,len); } //客户端发送数据完毕,结束Socket输出流的写入操作,告知服务器端,不再读了 socket.shutdownOutput(); //接收服务器端的回复 InputStream in=socket.getInputStream(); len=in.read(bytes); System.out.println(new String(bytes,0,len));//将字节数组内容转成字符串打印出来 //释放资源 fis.close(); socket.close(); } }
多文件上传案例
只需将服务器端代码封装改一下:
package com.oracle.TCPS; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.util.Random; public class UpLoad implements Runnable{ private Socket socket; public UpLoad(Socket socket){ this.socket=socket; } public void run() { FileOutputStream fos=null; try{ //显示哪个客户端Socket连接上了服务器 InetAddress ipObject = socket.getInetAddress();//得到IP地址对象 String ip = ipObject.getHostAddress(); //得到IP地址字符串 System.out.println("小样,抓到你了,连接我!!" + "IP:" + ip); //读取文件,明确数据源 InputStream in=socket.getInputStream(); //明确目的地 File file=new File("e:\\JPG"); //判断文件是否存在 if(!file.exists()){ file.mkdirs();//没有则创建文件夹 } //域名+毫秒值+6位随机数 String filename="oracle"+System.currentTimeMillis()+new Random().nextInt(6)+".jpg"; fos=new FileOutputStream(file+File.separator+filename); //复制,把Socket输入流中的数据,写入目的地的字节输出流中‘ int len=0; byte[] bytes=new byte[1024]; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len);//往服务器端写 } //回复客户端 OutputStream out=socket.getOutputStream(); out.write("上传成功".getBytes()); }catch (IOException ex){ ex.printStackTrace(); }finally{ try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
测试类:
package com.oracle.TCPS; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Demo { public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(7500);//明确端口号 while(true){ Socket socket=server.accept(); new Thread(new UpLoad(socket)).start();//匿名内部类 } } }
以上是关于等待唤醒机制,UDP通信和TCP通信的主要内容,如果未能解决你的问题,请参考以下文章