网络通信三要素:
1)ip地址
2)端口号
3)应该有一些规则(协议UDP/TCP)
举例:
我想和高圆圆聊天...
1)找到她,才能和她说话------>IP地址
2)假设找她了,怎么说呢?
对着她耳朵说话------->port端口号
3)要对她说:you are beautiful
假设不懂英文----->定义规则(协议)
IP地址:
192.168.10.1 (通过8421码将可以由0,1组成的一些数据)
点分十进制法:十进制.十进制.十进制.十进制 书写简单
Ip地址的分类:
IP地址的组成
IP地址 = 网络号码+主机地址
A类IP地址:第一段号码为网络号码,剩下的三段号码为本地计算机的号码
一般情况:国防部/大的国部门
B类IP地址:前二段号码为网络号码,剩下的二段号码为本地计算机的号码
一般情况:大学里面的多媒体教室
C类IP地址:前三段号码为网络号码,剩下的一段号码为本地计算机的号码
私人地址
A类 1.0.0.1---127.255.255.254 (1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址) (2)127.X.X.X是保留地址,用做循环测试用的。
B类 128.0.0.1---191.255.255.254 172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
C类 192.0.0.1---223.255.255.254 192.168.X.X是私有地址
D类 224.0.0.1---239.255.255.254
E类 240.0.0.1---247.255.255.254
127.0.0.1--->表示本地计算机的回环地址
端口号:0~65535有效端口号
0-1024属于保留端口
mysql:3306
协议:
UDP协议 --->UDP编程
不需要建立连接通道的
数据大小有限制
不可靠连接
执行效率高
TCP协议 ---->TCP编程
需要建立连接通道
数据大小无限制
可靠连接
执行效率低
打电话:看成TCP协议 建立连接通道
发短信:UDP协议 不需要建立连接通道
InetAddress:类表示互联网协议 (IP) 地址
如果一个类中没有构造方法,没有字段,只有成员方法?有什么特征
1)应该有一些静态功能(Math类,Arrays,Collections...)
2)可能符合一种单例模式(饿汉/懒汉)
3)该类中的某些静态成员方法的返回值是该类本身
举例
public class Demo{
public static Demo getDemo(){
new Demo() ;
}
}
常用方法:
public static InetAddress getByName(String host) throws UnknownHostException在给定主机名的情况下确定主机的 IP 地址。
参数:
主机名可以是机器名(如 "java.sun.com"),也可以是其 IP 地址的文本表示形式
public class InetAddRessDemo { public static void main(String[] args) throws UnknownHostException { //创建InetAddress对象,通过来获取ip地址 //在知道主机名的情况下 //InetAddress address = InetAddress.getByName("USER-20171205ZR") ; //IP地址的文本表示形式 //Ip地址对象 InetAddress address = InetAddress.getByName("192.168.10.1") ; //上面获取到了Ip地址 //public String getHostAddress()返回 IP地址字符串(以文本表现形式)。 String ip = address.getHostAddress() ; System.out.println(ip);//192.168.10.1 //public String getHostName()获取此 IP地址的主机名。 String name = address.getHostName() ; System.out.println(name); } }
UDP编程发送端的开发步骤:
1)创建发送端的Socket对象
2)创建数据,并打包
3)调用当前发送端Socket对象中的发送的方法
4)关闭资源
public class SendDemo { public static void main(String[] args) throws IOException { //1)创建发送端的Socket对象 //DatagramSocket:此类表示用来发送和接收数据报包的套接字。 //创建DataGramSocket对象 //public DatagramSocket(int port,InetAddress laddr) //这个构造方法,里面传递并不是Ip地址的文本形式 //构造数据报套接字并将其绑定到本地主机上任何可用的端口 DatagramSocket ds = new DatagramSocket() ; //2)创建数据,并打包 //DatagramPacket 数据报包 //DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) /** * buf - 包数据。 offset - 包数据偏移量。 length - 包数据长度。 address - 目的地址。 port - 目的端口号。 */ //有数据 byte[] bys = "hello,udp,我来了".getBytes() ; //获取包数据长度 int len = bys.length ; //获取ip地址对象 InetAddress address = InetAddress.getByName("192.168.10.1") ; //指定端口号 int port = 10086 ; DatagramPacket dp =new DatagramPacket(bys, 0, len, address, port) ; //3)调用当前发送端Socket对象中的发送的方法 /*public void send(DatagramPacket p) throws IOException从此套接字发送数据报包*/ ds.send(dp); //4)关闭资源 ds.close(); } }
UDP编程接收端的开发步骤:
1)创建Socket对象2)创建一个数据报包(接收容器)
3)调用Socket对象中的接收的方法
4)解析实际传递的数据
5)将解析的数据(ip,数据)展示在控制台上
6)关闭资源
注意:接收端不要运行多次,会出现异常:java.net.BindException: Address already in use: Cannot bind
public class ReceiveDemo { public static void main(String[] args) throws IOException { //1)创建Socket对象 //public DatagramSocket(int port)创建数据报包套接字对象并且将其绑定到本地主机上的指定端口 DatagramSocket ds = new DatagramSocket(10086); //2)创建一个数据报包(接收容器) //public DatagramPacket(byte[] buf, int length) byte[] bys = new byte[1024] ; int len = bys.length ; DatagramPacket dp = new DatagramPacket(bys, len) ; //3)调用Socket对象中的接收的方法 //public void receive(DatagramPacket p):接收数据报包 ds.receive(dp);//阻塞式方法 //获取ip地址文本形式 //public InetAddress getAddress() :返回ip地址对象 数据报包类:DataGramPacket InetAddress address = dp.getAddress() ; String ip = address.getHostAddress() ; //4)解析实际传递的数据 //public byte[] getData() :获取缓冲中实际数据(从接收容器中获取) byte[] bys2 = dp.getData() ; //public int getLength()返回将要发送或接收到的数据的长度。 int len2 = dp.getLength() ;//从接收容器中获取包的实际长度 String data = new String(bys2, 0, len2) ; //5)将解析的数据(ip,数据)展示在控制台上 System.out.println("当前接收到的数据是:"+data+",来自于"+ip); //6)关闭资源 ds.close(); } }
不过需要注意的是,若想进行通信,必须得先运行接收端,此时程序一直处于阻塞状态,然后再运行发送端,此时接收端就可以收到来自发送端发送的消息了。
对刚才的代码进行优化。
public class SendDemo { public static void main(String[] args) throws IOException { //创建发送端的Socket对象 DatagramSocket ds = new DatagramSocket() ; //创建数据报包对象 byte[] bys = "hello,udp,我来了".getBytes() ; DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.10.1"),12345) ; //发送数据 ds.send(dp); //释放资源 ds.close(); } }
public class ReceiveDemo { public static void main(String[] args) throws IOException { //创建接收端的Socket对象 DatagramSocket ds = new DatagramSocket(12345) ; //创建接收容器 //创建字节数组的大小1024或者1024的倍数 byte[] bys = new byte[1024] ; DatagramPacket dp = new DatagramPacket(bys, bys.length); //调用接收的方法 ds.receive(dp);//阻塞式方法 //解析数据,将数据展示在控制台 //获取ip地址 String ip = dp.getAddress().getHostAddress() ; //public byte[] getData() 获取缓冲区中实际的字节数 //public int getLength() 获取缓冲区中实际的长度 String s = new String(dp.getData(), 0,dp.getLength()) ; //展示控制台 System.out.println("from"+ip+"data is:"+s); //释放资源 ds.close(); } }
需求: 将发送端的数据变方式,并且多次发送
public class SendDemo { public static void main(String[] args) { try { // 创建Socket对象 DatagramSocket ds = new DatagramSocket(); // 键盘录入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line = null ; //读了一次 while((line=br.readLine())!=null){ //自定义结束条件 if("886".equals(line)) { break ; } byte[] bys = line.getBytes() ; int length = bys.length ; // 创建数据报包 DatagramPacket dp = new DatagramPacket(bys,length, InetAddress.getByName("192.168.10.1"), 10086); // 发送数据 ds.send(dp); } // 释放资源 ds.close(); } catch (IOException e) { e.printStackTrace(); } } }
public class ReceiveDemo { public static void main(String[] args) { try { //创建接收端的Socket对象 DatagramSocket ds = new DatagramSocket(10086); while(true) { //需要创建接收容器 byte[] bys = new byte[1024] ; DatagramPacket dp = new DatagramPacket(bys, bys.length) ; //接收 ds.receive(dp); //解析数据 String ip = dp.getAddress().getHostAddress() ; String s = new String(dp.getData(), 0, dp.getLength()) ; System.out.println("from" +ip +"data is:"+s); //释放资源,接收端不停地接收数据,所以不应该该关闭 // ds.close(); } } catch (IOException e) { e.printStackTrace(); } } }利用多线程实现udp网络通信
多线程:
Runable接口的方式
SendThread implements Runnable{
}
发送端的线程
接收端的线程
主线程
只需要需一个窗口
public class SendThread implements Runnable { private DatagramSocket ds ; public SendThread(DatagramSocket ds) { this.ds = ds ; } @Override public void run() { try { //使用字符缓冲输入流 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ; String line = null ; //不断发送数据 while((line=br.readLine())!=null) { //自定义结束条件 if("over".equals(line)) { break ; } byte[] bys = line.getBytes() ; //创建数据报包 DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.10.1"), 10086) ; //发送数据 ds.send(dp); } //释放 ds.close(); }catch(IOException e) { e.printStackTrace(); } } }
public class ReceiveThread implements Runnable { private DatagramSocket ds; public ReceiveThread(DatagramSocket ds) { this.ds = ds; } @Override public void run() { try { while(true) { //需要创建接收容器 byte[] bys = new byte[1024] ; DatagramPacket dp = new DatagramPacket(bys, bys.length) ; //接收 ds.receive(dp); //解析数据 String ip = dp.getAddress().getHostAddress() ; String s = new String(dp.getData(), 0, dp.getLength()) ; System.out.println("from" +ip +"data is:"+s); //释放资源,接收端不停地接收数据,所以不应该该关闭 // ds.close(); } }catch(IOException e) { e.printStackTrace(); } } }
public class ChatRoom { public static void main(String[] args) throws Exception { //分别创建发送端和接收端的Socket对象 DatagramSocket sendSocket = new DatagramSocket() ; DatagramSocket receiveSocket = new DatagramSocket(10086) ; //创建资源对象 SendThread st = new SendThread(sendSocket) ; ReceiveThread rt = new ReceiveThread(receiveSocket) ; //创建线程对象 Thread t1 = new Thread(st) ; Thread t2 = new Thread(rt) ; //启动线程 t1.start(); t2.start(); } }