1、网络通讯要素
(1)IP地址 (2)端口号 (3)传输协议 |
一个主机向另一个主机发送数据,首先这个主机要找到对方的IP地址,这是在网络层完成的,数据要发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识,为了方便称呼这些数字,叫做端口,两个主机通信,需要定义通讯规则,这个通信规则成为协议。
我们在做网络编程开发,其实就是在网络层和传输层之间活动,每一层都有自己的规则协议,传输层常见的就是TCP,UDP,网络层最常见的协议就是IP
1.1、IP地址
(1)网络层中设备的标识 (2)不易记忆,可用主机名 (3)本地回环地址:127.0.0.1,主机名:localhost |
为了便于应用,IP封装成了一个对象,类名为:InetAddress
InetAddress的主要方法
(1)static InetAddress getByName(String host) 在给定主机名的情况下确定主机的IP地址 (2)String getHostAddress() 返回IP地址的字符串 (3)String getHostName() 获取此IP地址的主机名 (4)static InetAddress getLocalHost():返回本地主机 |
例:
package com.itheima.net; import java.net.InetAddress; public class NetDemo1 { public static void main(String[] args) throws Exception{ //获取一个InetAddress对象 InetAddress ia=InetAddress.getByName("www.baidu.com"); //打印IP地址 System.out.println(ia.getHostAddress()); //打印此IP地址所对象的主机名 System.out.println(ia.getHostName()); } } |
1.2、端口号
端口号就是一个数字标识,用于标识进程的逻辑地址,有效端口0-65535,0-1024为系统使用,没有必要封装成对象
1.3、传输协议
在传输层常用的两个协议为UDP和TCP
UDP的特点
(1)将数据及源和目的封装成数据包,不需要建立连接 (2)每个数据包的大小限制在64K (3)由于是无连接,所以是不可靠协议 (4)不需要建立连接,速度快 |
例如飞秋,视频会议,桌面共享都是UDP传输
TCP的特点
(1)建立连接,形成传输的数据的通道 (2)在连接中进行大数据的传输 (3)通过三次握手完成连接,是可靠的协议 (4)必须连接,效率较低 |
2、Socket
Socket是为网络服务提供的一种机制。
(1)通信两端都有Socket (2)网络通信其实就是Socket间的通信 (3)数据在两个Socket间通过IO传输 |
3、UDP详解
UDP传输涉及的几个重要的对象
(1)DatagramSocket:此类表示用来发送和接收数据报包的套接字,主要方法如下:
(1)send(DatagramPacket p):从套接字发送数据报包 (2)Receive(DataParamPacket p):从此套接字接收数据报包 |
(2)DatagramPacket:表示数据报包
数据报包用来实现无连接包投递服务,其中的接收和发送的构造方法
(1)DatagramPacket(byte[]buf,int length,InetAddress address,int port) 用来将长度为length的包发送到指定主机的指定端口 (2)DatagramPacket(byte[]buf,int length) 用来接收长度为length的数据包 |
例:通过UDP传输方式,将一段文字数据发送出去
发送端:(思路)
(1)建立UDPSocket服务 (2)提供数据,并将数据封装到数据包中 (3)通过Socket服务的发送功能,将数据发送出去 (4)关闭资源 |
代码如下:
package com.itheima.net; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPDemo1 { public static void main(String[] args)throws Exception { //创建连接,建立DatagramSocket对象 DatagramSocket ds=new DatagramSocket(); //定义一个字节数组 byte[]buf="Hello heima".getBytes(); //把数据封装成数据包 DatagramPacket dp=new DatagramPacket( buf, buf.length,InetAddress.getByName("192.168.14.3"),10002); //发送 ds.send(dp); //关闭资源 ds.close(); } } |
接收端:(思路)
(1)建立UDP Socket服务,通常会监听一个端口,其实即使给这个接收网络应用程序定义数字标识 (2)定义一个数据包,因为要存储接收到的字节数据,因为数据包对象中有更多功能可以提取字节数据张的不同数据资源 (3)通过socket服务的receive方法将受到的数据存储到已定义好的数据包中 (4)通过数据包的对象的特有功能,将这些不同的数据取出,打印在控制台 (5)关闭资源 |
代码如下:
package com.itheima.net; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPSend1 { public static void main(String[] args)throws Exception { //创建连接,建立DatagramSocket对象 DatagramSocket ds=new DatagramSocket(); //定义一个字节数组 byte[]buf="Hello heima".getBytes(); //把数据封装成数据包 DatagramPacket dp=new DatagramPacket( buf, buf.length,InetAddress.getByName("192.168.14.3"),10002); //发送 ds.send(dp); //关闭资源 ds.close(); } } |
如果接受端不断的接收数据,代码修改如下:
public static void main(String[] args)throws Exception { //创建连接,建立DatagramSocket对象 DatagramSocket ds=new DatagramSocket(); while(true){ //定义一个字节数组 byte[]buf="Hello heima".getBytes(); //把数据封装成数据包 DatagramPacket dp=new DatagramPacket( buf, buf.length,InetAddress.getByName("192.168.14.3"),10002); //发送 ds.send(dp); } } |
此时不能再关闭资源了,也不能把DatagramSocket对象房间while循环中,如果放进去就会出现BindException异常
应用一:利用键盘录入的发送数据
package com.itheima.net; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; //接收端 class UDPSend2{ public static void main(String[] args)throws Exception { //建立连接,创建DatagramSocket对象 DatagramSocket ds=new DatagramSocket(); //创建一个键盘输入流对象 BufferedReader bufr=new BufferedReader( new InputStreamReader(System.in)); String line=null; //读取数据 while((line=bufr.readLine())!=null){ //如果读取到了over,则停止 if("over".equalsIgnoreCase(line)){ break; } //把读取到的字符串转换成字节数组 byte[]buf=line.getBytes(); //把数据封装数据包中 DatagramPacket dp=new DatagramPacket( buf, buf.length,InetAddress.getByName("localhost"),10008); //发送数据 ds.send(dp); } //关闭资源 ds.close(); } } //接收端 class UDPReceive2{ public static void main(String[] args)throws Exception { //建立连接 DatagramSocket ds=new DatagramSocket(10008); //循环的接收独享 while(true){ //定义一个字节数组用于接收对象 byte[]buf=new byte[1024]; //把接收的对象封装到数据包中 DatagramPacket dp=new DatagramPacket(buf, buf.length); //接收数据 ds.receive(dp); //获取ip地址 String ip=dp.getAddress().getHostAddress(); //获取数据 String data=new String(dp.getData(),0,dp.getLength()); System.out.println(ip+"::"+data); } } } |
应用二:UDP聊天
编写这个聊天程序,有收到数据的部分,和发送数据的部分,这两个部分需要同时执行,那就需要用到多线程技术,一个线程控制接收,一个线程控制发送,因为接收和发送动作时不一致的,所以要定义两个run方法,而且这两个方法要封装到不同的类中
代码如下:
package com.itheima.net; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPDemo3 { public static void main(String[] args) { try { //创建一个发送DatagramSocket对象 DatagramSocket sendDs=new DatagramSocket(); //创建一个接收DatagramSocket对象 DatagramSocket receiveDs=new DatagramSocket(10009); //开启发送的线程 new Thread(new Send(sendDs)).start(); //开启接收的线程 new Thread(new Receive(receiveDs)).start(); } catch (Exception e) { e.printStackTrace(); } } } //定义一个发送端,实现Runnable接口 class Send implements Runnable{ //接收一个DatagramSocket private DatagramSocket ds=null; public Send(DatagramSocket ds){ this.ds=ds; } @Override public void run() { BufferedReader bufr=null; try { //从键盘读取数据 bufr=new BufferedReader(new InputStreamReader(System.in)); //定义一个字符串用于接收数据 String line=null; while((line=bufr.readLine())!=null){ //提供数据,并封装到数据包中 byte[]buf=line.getBytes(); DatagramPacket dp=new DatagramPacket( buf, buf.length,InetAddress.getByName("localhost"),10009); //发送数据 ds.send(dp); } } catch (Exception e) { throw new RuntimeException("发送失败"); }finally{ try { if(bufr!=null)//如果不为空,则关闭 bufr.close(); if(ds!=null)//如果不为空,关闭 ds.close(); } catch (Exception e2) { e2.printStackTrace(); } } } } //定义一个接收端,实现Runnable接口 class Receive implements Runnable{ //接收一个DatagramSocket private DatagramSocket ds=null; public Receive(DatagramSocket ds){ this.ds=ds; } @Override public void run() { try { while(true){ //定义一个字节存储数据 byte[]buf=new byte[1024]; //创建一个数据包对象 DatagramPacket dp=new DatagramPacket(buf, buf.length); //接收数据 ds.receive(dp); //获取IP String ip=dp.getAddress().getHostAddress(); //获取封装的数据 String data=new String(dp.getData(),0,dp.getLength()); System.out.println(ip+":"+data); } } catch (Exception e) { throw new RuntimeException("发送失败"); } } } |