数据的传输
1、协议:TCP与UDP协议
1.TCP协议:面向连接 ,安全可靠,效率低下,类似于打电话,有三次握手
2.UDP协议:非面向连接,不可靠,效率高,类似于短信
2、传输(包括:封装与拆分),先封装,后拆分。
特点:
1)UDP:
把数据打包
数据大小有限制(64k)
不建立连接
速度快,但可靠性低
2)TCP:
建立连接通道
数据大小无限制
速度慢,但是可靠性高
TCP/IP协议分层
应用层:
向用户提供一组常用的应用程序,比如电子邮件、文件传输访问、远程登录等。远程登录TELNET使用TELNET协议提供在网络其它主机上注册的接口。TELNET会话提供了基于字符的虚拟终端。文件传输访问FTP使用FTP协议来提供网络内机器间的文件拷贝功能。
传输层:
提供应用程序间的通信。其功能包括:一、格式化信息流;二、提供可靠传输。为实现后者,传输层协议规定接收端必须发回确认,并且假如分组丢失,必须重新发送。
网络层 :
负责相邻计算机之间的通信。其功能包括三方面:
一、处理来自传输层的分组发送请求,收到请求后,将分组装入IP数据报,填充报头,选择去往信宿机的路径,然后将数据报发往适当的网络接口。
二、处理输入数据报:首先检查其合法性,然后进行寻径–假如该数据报已到达信宿机,则去掉报头,将剩下部分交给适当的传输协议;假如该数据报尚未到达信宿,则转发该数据报。
三、处理路径、流控、拥塞等问题。
网络接口层:
是TCP/IP软件的最低层,负责接收IP数据报并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层。
1、IP地址及端口号
package ip;
/**
* 没有封装端口
*/
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Demo01 {
public static void main(String[] args) throws UnknownHostException {
//使用getLocationHost方法创建InetAddree对象
InetAddress addr = Inet4Address.getLocalHost();
System.out.println(addr.getHostAddress());//返回ip地址
System.out.println(addr.getHostName());//输出计算机名
//根据域名得到InetAddress对象
addr = InetAddress.getByName("www.163.com");
System.out.println(addr.getHostAddress());//返回服务器的ip
System.out.println(addr.getHostName());
//根据ip得到InetAddress对象
addr = InetAddress.getByName("202.201.14.181");
System.out.println(addr.getHostAddress());//返回服务器的ip
System.out.println(addr.getHostName());//地址不存在或者为了安全性DNS域名不能解析 就返回ip 182.140.147.57
}
}
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
/**
* 封装端口:在InetAddress基础上+端口
*/
public class Demo02 {
public static void main(String[] args) throws UnknownHostException {
InetSocketAddress address = new InetSocketAddress("localhost",9999);
//address = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),9999);
System.out.println(address.getHostName());
System.out.println(address.getPort());
InetAddress addr = address.getAddress();//获得地址
System.out.println(addr.getHostAddress());//返回:IP地址
System.out.println(addr.getHostName());//输出计算机名
}
}
2、URL,统一资源定位符,它是一个具体的URI。
URL由四部分组成:协议,存放资源的主机域名,端口号,资源文件名
URL(String protocol, String host, int port, String file)
package URL;
import java.net.MalformedURLException;
import java.net.URL;
public class Demo01 {
public static void main(String[] args) throws MalformedURLException {
//绝对路径构建
URL url = new URL("http://www.baidu.com:80/index.html#bb?uname=liguodong");
//url = new URL("http://www.baidu.com:80/index.html?uname=liguodong");
System.out.println("协议:"+url.getProtocol());
System.out.println("域名:"+url.getHost());
System.out.println("端口:"+url.getPort());
System.out.println("资源:"+url.getFile());
System.out.println("相对路径的资源:"+url.getPath());//用得比较多
System.out.println("緢点:"+url.getRef());//緢点 bb?uname=liguodong
System.out.println("参数:"+url.getQuery());//参数:存在緢点返回null,不存在,返回正确
url = new URL("http://www.baidu.com:80/a/");
url = new URL(url,"b.txt");
System.out.println(url.toString());
}
}
package URL;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
/**
* 获取资源:源代码
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
URL url = new URL("http://www.baidu.com");//有时,如果没有资源,主页即是默认资源。
//获取资源
//这种方式会出现乱码出现乱码的情况分别是字节数不够或者编码不统一,这种情况明显是编码不统一。
//因此可以使用直接将平台的编码改掉,或者使用转换流
/*InputStream is = url.openStream();
byte[] flush = new byte[1024];
int len=0;
while(-1!=(len=is.read(flush)))
{
System.out.println(new String(flush,0,len));
}
is.close();*/
BufferedReader br = new BufferedReader(
new InputStreamReader(url.openStream(),"utf-8"));
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("baidu.html"),"utf-8"));
String msg = null;
while((msg=br.readLine())!=null)
{
System.out.println(msg);
System.out.println();
bw.append(msg);
bw.newLine();
}
bw.flush();
bw.close();
br.close();
}
}
3、UDP通信:以数据为中心,非面向连接,不安全,数据可能丢失 效率高
DatagramPacket:此类表示数据报包。
DatagramSocket:类表示用来发送和接收数据报包的套接字。
UDP是传输层协议,和TCP协议处于一个分层中,但是与TCP协议不同,UDP协议并不提供超时重传,出错重传等功能,也就是说其是不可靠的协议。
整体设计
1.客户端
1.1创建客户端
1.2创建数据(字节数组)
1.3打包(DatagramPacket+服务器地址及端口号)
1.4发送
1.5释放数据
2.服务器端
2.1创建服务器(DatagramSocket类+制定端口号)
2.2准备接收容器(字节数组)
2.3包(接收数据)
2.4分析
2.5释放资源
package UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 服务端
* 1、创建服务端+端口
* 2、准备接收容器
* 3、封装成包
* 4、接收数据
* 5、分析数据
* 6、释放资源
*
* DatagramPacket(byte[] buf, int length)
* 构造 DatagramPacket,用来接收长度为 length 的数据包。
*
* DatagramPacket(byte[] buf, int length, InetAddress address, int port)
* 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
*
*/
public class MyServer {
public static void main(String[] args) throws IOException {
//1、创建服务端+端口
DatagramSocket server = new DatagramSocket(8888);
//2、准备接收容器
byte[] container = new byte[1024];
//3、封装成包 DatagramPacket(byte[] buf, int length)构造接收数据包
DatagramPacket packet = new DatagramPacket(container, container.length);
//4、接收数据
server.receive(packet);
//5、分析数据
byte[] data = packet.getData();
int len = packet.getLength();
System.out.println(new String(data,0,len));
//6、释放资源
server.close();
}
}
package UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
/**
* 客户端
*1、创建客户端+端口
*2、准备数据
*3、打包(发送地点及端口)
*4、发送
*5、释放
*
*/
public class MyClient {
public static void main(String[] args) throws IOException {
//1、创建客户端+端口
DatagramSocket client = new DatagramSocket(6666);
//2、准备数据
String msg = "模拟UDP过程";
byte[] data = msg.getBytes();
//3、打包(指定发送的地点及端口)
//DatagramPacket(byte[] buf, int length, SocketAddress address) 构造发送数据报包
DatagramPacket packet = new DatagramPacket(data, data.length,
new InetSocketAddress("localhost",8888));
//4、发送
client.send(packet);
//5、释放
client.close();
}
}
非面向连接的协议,当服务器打开,服务器能收到数据,当服务器关闭,客户端运行也不会报错,数据也相应的被丢失了。
如果我们发送的数据不是字节数组,而是一个浮点数,因此我们需要进行数据类型转换。本例值得回味。
package UDP;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
/**
* 89.12 数据的发送要字节数组,因此将double类型转成字节数组
* 客户端
*1、创建客户端+端口
*2、准备数据 double类型转成字节数组 字节数组输出流 类型转换问题
*3、打包(发送地点及端口)
*4、发送
*5、释放
*/
public class Client {
public static void main(String[] args) throws IOException {
//1、创建服务器+端口
DatagramSocket client = new DatagramSocket(6666);
//2、准备数据
double num = 89.12;
byte[] data = convert(num);
//3、打包(发送的地点及端口)
DatagramPacket packet = new DatagramPacket(data, data.length,
new InetSocketAddress("localhost",8888));
//4、发送
client.send(packet);
//5、释放
client.close();
}
/**
* 字节数组输出流ByteArrayOutputStream+DataOutputStream数据输出流
* @param num
* @return
* @throws IOException
*/
public static byte[] convert(double num) throws IOException
{
byte[] data = null;
//有新增方法,不能使用多态
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeDouble(num);
dos.flush();
//获取数据
data = bos.toByteArray();
dos.close();
bos.close();//此方法关闭 ByteArrayOutputStream无效。
return data;
}
}
package UDP;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 89.12 数据+类型 用 IO流
* 服务端
* 1、创建服务端+端口
* 2、准备接受容器
* 3、封装成包
* 4、接受数据
* 5、分析数据 字节数组-->double
* 6、释放资源
*/
public class Server {
public static void main(String[] args) throws IOException {
//1、创建服务端+端口
DatagramSocket server = new DatagramSocket(8888);
//2、准备接受容器
byte[] container = new byte[1024];
//3、封装成包 DatagramPacket(byte[] buf, int length);
DatagramPacket packet = new DatagramPacket(container, container.length);
//4、接受数据
server.receive(packet);
//5、分析数据
double data = convert(packet.getData());
System.out.println(data);
//6、释放资源
server.close();
}
/**
* 字节数组输入类ByteArrayInputStream+DataInputStream数据输入流
* @param data
* @return
* @throws IOException
*/
public static double convert(byte[] data) throws IOException
{
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));
double num = dis.readDouble();
dis.close();
return num;
}
}
4、TCP通信: 面向连接,安全可靠,效率低下
ServerSocket:此类实现服务器套接字。
Socket:此类实现客户端套接字。
TCP是一个面向连接的协议,所以在连接双方发送数据之前,都需要首先建立一条连接。这和前面讲到的协议完全不同。前面讲的所有协议都只是发送数据而已,大多数都不关心发送的数据是不是送到,UDP尤其明显,从编程的角度来说,UDP编程也要简单的多—-UDP都不用考虑数据分片。
书中用telnet登陆退出来解释TCP协议连接的建立和中止的过程,可以看到,TCP连接的建立可以简单的称为三次握手,而连接的中止则可以叫做四次握手。
package TCP;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 必须先启动再连接
* 1、创建服务器 指定端口 ServerSocket(int port)
* 2、接收客户端的连接 阻塞式
* 3、发送数据+接收数据
*/
public class Server {
public static void main(String[] args) throws IOException {
//1、创建服务器 指定端口
ServerSocket server = new ServerSocket(8888);//不同协议端口可以重复,同一协议端口不能重复
//2、接收客户端的连接
Socket socket = server.accept();//这里会等待连接
System.out.println("一个客户端建立连接");
//2、发送数据
String msg = "欢迎使用";
//3、输出流
/*BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
bw.write(msg);
bw.newLine();//一定要加行结束符,不然读不到数据
bw.flush();*/
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(msg);
dos.flush();
}
}
package TCP;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 1、创建客户端 必须指定服务器+端口 此时就在连接
* Socket(String host,int port)
* 2、接收数据+发送数据
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、创建客户端 必须指定服务器+端口 此时就在连接
Socket client = new Socket("localhost",8888);//指定服务器的地址与端口,客户端的端口由服务器自动分配
//2、接收数据
/*BufferedReader br = new BufferedReader(
new InputStreamReader(client.getInputStream()));
String echo = br.readLine();//逐行执行,阻塞式方法
System.out.println(echo);*/
DataInputStream dis = new DataInputStream(client.getInputStream());
String echo = dis.readUTF();
System.out.println(echo);
}
}
使用TCP时,必须要打开服务器,不然运行客服端会出异常。并且客户端必须要指定服务器的地址与端口,客户端的端口由服务器自动分配。