网络通信的三要素:IP地址,端口号,协议。
IP地址:
IPV4:4个0-255的数组来表示,每个数字占一个byte,一个IP地址占32位内存,约有43亿个IP地址。前2个或3个表示子网号。
比如,当前计算机地址为10.10.22.90,子网号为10.10.22,90表示当前计算机的标识,最后一位理论上能产生256个数字,但是其中0和255有特殊含义,0表示子网号,255表示广播地址,所以有效标识符有254个。
IPV6:128位,十六进制数据。
特殊IP地址:127.0.0.1,本地回环地址,表示的是当前这个计算机自己,和 " localhost " 等价。
DNS:域名解析器。
InetAddress类是用来表示IP地址对象的,该类不能直接创建对象。类中有静态方法。
static InetAddress |
getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。 |
static InetAddress[] |
getAllByName(String host) 在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。 |
static InetAddress |
getLocalHost() 返回本地主机 |
String |
getHostName() 获取此 IP 地址的主机名。 |
String |
getHostAddress() 返回 IP 地址字符串(以文本表现形式)。 |
import java.net.InetAddress;
public class Demo3 {
public static void main(String[] args)throws Exception {
InetAddress add1=InetAddress.getByName("www.baidu.com");//获取指定主机的InetAddress对象
InetAddress add2=InetAddress.getLocalHost();//获取本机的InetAddress对象
InetAddress[] add3=InetAddress.getAllByName("www.youtube.com");//获取指定主机的所有InetAddress对象
String s1=add1.getHostName();//获取InetAddress对象的主机名
String s2=add1.getHostAddress();//获取InetAddress对象的地址
System.out.println("add1---"+add1);
System.out.println("add2----"+add2);
for(InetAddress in:add3)
System.out.println("add3---ip地址"+in.getHostAddress()+"主机名"+in.getHostName());
System.out.println("s1---"+s1);
System.out.println("s2---"+s2);
}
}
端口号:用2个byte表示的一个数字,这个数字用来表示系统中每一个进程的表示。范围是0-65535(2^16),也就是说最多可以有65536个程序同时运行。程序启动时,系统会默认随机分配一个端口号,当程序运行结束后会释放这个端口号,可以自己指定端口号。
0-1024间的端口号被系统占用,不可用。
常用端口号:MySQL-3306,TomCat-8080,Oracle-1521
协议
设备间通信时,对数据封装和解析的规则。
分层处理:
应用层:HTTP,HTTPS
传输层:UDP,TCP
网络层:IP协议
物理层:硬件
UDP:面向无连接,不可靠协议,速度快,数据不能超过64K
TCP:面向有链接,可靠协议,速度慢,可以传输大文件,采用三次握手确认连接。
UDP协议:
DatagramSocket:用来表示发送和接收数据包的套接字。
构造方法摘要 | |
---|---|
|
DatagramSocket() //计算机随机指定端口构造数据报套接字并将其绑定到本地主机上任何可用的端口。 |
protected |
DatagramSocket(DatagramSocketImpl impl) 创建带有指定 DatagramSocketImpl 的未绑定数据报套接字。 |
|
DatagramSocket(int port) //自己指定端口创建数据报套接字并将其绑定到本地主机上的指定端口。 |
|
DatagramSocket(int port, InetAddress laddr) 创建数据报套接字,将其绑定到指定的本地地址。 |
|
DatagramSocket(SocketAddress bindaddr) 创建数据报套接字,将其绑定到指定的本地套接字地址。 |
DatagramPacket:打包和拆包对象。打包的参数都有4个(byte数组,数组长度,IP地址,端口号),拆包的参数只有2个(byte数组,数组长度)顶多再加上一个数组的offset
构造方法摘要 | |
---|---|
DatagramPacket(byte[] buf, int length) 构造 DatagramPacket ,用来 接收 长度为 length 的数据包。 |
|
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 的包 发送 到指定主机上的指定端口号。 |
|
DatagramPacket(byte[] buf, int offset, int length) 构造 DatagramPacket ,用来 接收 长度为 length 的包,在缓冲区中指定了偏移量。 |
|
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 偏移量为 offset 的包 发送 到指定主机上的指定端口号。 |
|
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) 构造数据报包,用来将长度为 length 偏移量为 offset 的包 发送 到指定主机上的指定端口号。 |
|
DatagramPacket(byte[] buf, int length, SocketAddress address) 构造数据报包,用来将长度为 length 的包 发送 到指定主机上的指定端口号。 |
发送端:
- 创建端点对象
- 打包
- 发包
- 关闭端点对象
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//发送端
public class Demo4 {
public static void main(String[] args)throws Exception {
DatagramSocket dgs=new DatagramSocket();//1.创建端点对象,可以指定端口也可以不指定
byte[] buf="这个字节数组是用来打包的".getBytes();//2.打包,用来读取数据的数组
InetAddress in=InetAddress.getByName("localhost");//发送到哪个IP地址?
int len=buf.length;//数组长度
int port=8989;//通过哪个端口号发送?
DatagramPacket dgp=new DatagramPacket(buf, len, in, port);
dgs.send(dgp);//3.发包
System.out.println("发送完成");
System.out.println("-------------------------");
//////////////////////////接收回执,转为接收端//////////////////////////////////
byte[] by=new byte[30];
DatagramPacket dg=new DatagramPacket(by, by.length);//接包
dgs.receive(dg);
//拆包
String s1=new String(dg.getData());
int length=dg.getLength();
String add=dg.getAddress().getHostAddress();
int port2=dg.getPort();
System.out.println(s1);
System.out.println(length);
System.out.println(add);
System.out.println(port2);
System.out.println("----已收到回执-----");
dgs.close();//4.关闭端点对象
}
}
接收端:
- 创建端点对象
- 接包
- 拆包
- 关闭端点对象
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//接收端
public class Demo5 {
public static void main(String[] args)throws Exception {
DatagramSocket dgs=new DatagramSocket(8989);//1.创建端点对象,接收端必须指定端口,就是发送端打包的时候的端口
//2.接包,发包接包都是DatagramPacket对象的功能,所以要先创建这个对象
byte[] buf=new byte[30];
int length=buf.length;
DatagramPacket dgp=new DatagramPacket(buf, length);//接收端只需要两个参数就可以了,发送端需要四个参数
dgs.receive(dgp);//开始接包!!!
//3.拆包
String data=new String(dgp.getData());//获取发送端发送的数据,由于接受的是字节数组,所以要转换成字符串
InetAddress address=dgp.getAddress();//获取发送端的IP地址,返回的是InetAddress对象
String add=address.getHostAddress();//获取IP地址的字符串形式
int port=dgp.getPort();//获取发送端的端口号,如果发送端定义了值就返回他,如果没定义过就返回计算机随机分配的值
int len=dgp.getLength();//返回将要发送或接收到的数据长度,发送端也可以调用这个方法
System.out.println(data);
System.out.println(add);
System.out.println(port);
System.out.println(len);
System.out.println("---接收完成---");
////////////////////////////发送回执,转变为发送端////////////////////////////////
//端点对象已经创建过了,可以直接打包发包
byte[] by="信息已收到!".getBytes();
DatagramPacket dg=new DatagramPacket(by, by.length,address,port);//打包,打包的方法都有四个参数
dgs.send(dg);//发包
System.out.println("----回执已发送成功----");
dgs.close();//4.关闭端点对象
}
}
TCP
注意!!!不管是客户端还是服务端,只要是在使用循环读取另外一方发来的数据,发送方 一定要记得最后使用 s.shutdownOutput() 通知接收数据的一方,数据发送完成。否则接收数据一方的循环就无法停止。
客户端
- 创建Socket对象
- 获取输出流
- 向服务端写入数据
- 关闭Socket对象,只需要关闭Socket和自己定义的输入输出流即可,注意服务端获取到的Socket对象也需要关闭
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
//上传文件
public class Client2 {
public static void main(String[] args) throws Exception{
Socket so=new Socket("localhost", 8989);
OutputStream os=so.getOutputStream();//outputStream是抽象类,但是他自己也有方法,可以直接用
FileInputStream fis=new FileInputStream("D:\\Items.txt");
byte[] by=new byte[1024];
int len=0;
while ((len=fis.read(by))!=-1) {
os.write(by, 0, len);
}
System.out.println("数据写出已完毕!");
so.shutdownOutput();
// os.close();通道的输出流不能关闭,否则连接就断了
fis.close();
///////////////////////接收回执信息///////////////////////////////
InputStream in=so.getInputStream();
byte[] byt=new byte[20];
in.read(byt);
System.out.println(new String(byt));
// in.close();通道只需要关闭socket就行,其他手动添加的输出流自行关闭
so.close();
}
}
服务端
- 创建ServerSocket对象
- 接收客户端请求,并与客户端建立连接
- 获取通道中的输入流
- 读取客户端发送的数据
- 关闭,只需要关闭和ServerSocket对象和自己定义的输入输出流即可,获取到的客户端的Socket对象也要关闭!
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//接收上传的文件并保存在本地
public class Server2 {
public static void main(String[] args) throws Exception{
ServerSocket ss=new ServerSocket(8989);
Socket so=ss.accept();//接收请求,与客户端建立连接
InputStream is=so.getInputStream();
FileOutputStream fos=new FileOutputStream("D:\\upload.txt");
byte[] by=new byte[1024];
int len=0;
while ((len=is.read(by))!=-1) {
fos.write(by, 0, len);
}
// so.shutdownInput();//结束输入流
System.out.println("文件读取结束,已保存本地");
////////////////////////////回执////////////////////////////
OutputStream os=so.getOutputStream();
os.write("文件上传成功!".getBytes());
System.out.println("回执已发送");
// is.close();不需要关闭
// os.close();不需要关闭
so.close();
fos.close();
ss.close();
}
}