Java 进阶:什么是 TCP-IP?如何运用!(1)
2. TCP 协议
TCP 协议是面向连接的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在 TCP 连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
第一次握手,客户端向服务器端发出连接请求,等待服务器确认第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求第三次握手,客户端再次向服务器端发送确认信息,确认连接
三、UDP 通信
1. 数据包和发送对象
DatagramPacket 数据包 的作用就如同是“集装箱”,可以将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还需要有码头。
在程序中需要实现通信只有 DatagramPacket 数据包也同样不行,为此 JDK 中提供的一个 DatagramSocket 类。
DatagramSocket 类的作用就类似于码头,使用这个类的实例对象就可以 发送和接收 DatagramPacket 数据包
UDP 发送端
实现 UDP 协议的发送端
实现封装数据的类 java.net.DatagramPacket 将数据包装
实现数据传输的类 java.net.DatagramSocket 将数据包发出去
实现步骤
1.创建 DatagramPacket 对象,封装数据, 接收的地址和端口 2.创建 DatagramSocket 对象 3.调用 DatagramSocket 类方法 send ,发送数据包 4.关闭资源
DatagramPacket 构造方法
DatagramPacket(byte[] buf, int length, InetAddress address, int port):字节数组,发送多少,IP 地址,端口号
DatagramSocket 构造方法:
public class UDPSend {public static void main(String[] args) throws IOException{//创建数据包对象,封装要发送的数据,接收端 IP,端口 byte[] date = "你好 UDP".getBytes();//创建 InetAddress 对象,封装自己的 IP 地址 InetAddress inet = InetAddress.getByName("127.0.0.1");DatagramPacket dp = new DatagramPacket(date, date.length, inet,6000);//创建 DatagramSocket 对象,数据包的发送和接收对象 DatagramSocket ds = new DatagramSocket();//调用 ds 对象的方法 send,发送数据包 ds.send(dp);//关闭资源 ds.close();}}
3. UDP 接收端
实现 UDP 接收端
实现封装数据包 java.net.DatagramPacket 将数据接收
实现数据传输 java.net.DatagramSocket 接收数据包
实现步骤
1.创建 DatagramSocket 对象,绑定端口号,要和发送端端口号一致 2.创建字节数组,接收发来的数据 3.创建数据包对象 DatagramPacket
5.拆包:
6. 关闭资源
public class UDPReceive {public static void main(String[] args)throws IOException {//创建数据包传输对象 DatagramSocket 绑定端口号 DatagramSocket ds = new DatagramSocket(6000);//创建字节数组 byte[] data = new byte[1024];//创建数据包对象,传递字节数组 DatagramPacket dp = new DatagramPacket(data, data.length);//调用 ds 对象的方法 receive 传递数据包 ds.receive(dp);
//获取发送端的 IP 地址对象 String ip=dp.getAddress().getHostAddress();
//获取发送的端口号 int port = dp.getPort();
//获取接收到的字节个数 int length = dp.getLength();System.out.println(ip+" "+port+":"+new String(data,0,length));ds.close();}}
四、TCP 通信
1. 概述
TCP 通信同 UDP 通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建 socket 对象。
区别在于,UDP 中只有 发送端 和 接收端,不区分 客户端 与服务器 端,计算机之间可以任意地发送数据。
而 TCP 通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。
在 JDK 中提供了两个类用于实现 TCP 程序,一个是 ServerSocket 类,用于表示服务器端,一个是 Socket 类,用于表示客户端。
通信时,首先创建代表服务器端的 ServerSocket 对象,该对象相当于开启一个服务,并等待客户端的连接,然后创建代表客户端的 Socket 对象向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信。
一个 socket 连接如何唯一标识?源端(ip+端口号)、目的(ip+端口号)唯一确定一个 tcp 连接
2. TCP 的客户端程序
实现 TCP 客户端,连接到服务器和服务器实现数据交换
构造方法:
构造方法只要运行,就会和服务器进行连接,连接失败就抛出异常
客户端服务器数据交换,必须使用套接字对象 Socket 中的获取的 IO 流,自己 new 的流是不行的
public class TCPClient {public static void main(String[] args) throws IOException {//创建 Socket 对象,连接服务器 Socket socket = new Socket("127.0.0.1", 8888);//通过客户端的套接字对象 Socket 方法,获取字节输出流将数据写向服务器 OutputStream out = socket.getOutputStream();out.write("服务器 ok!".getBytes()
);
//读取服务器发回的数据,使用 socket 套接字对象中字节输入流 InputStream in = socket.getInputStream();byte[] data = new byte[1024];int len = in.read(data);System.out.println(new String(data,0,len));
socket.close();}}
3. TCP 的服务器程序 & accept 方法
public class TCPServer {public static void main(String[] args) throws IOException {ServerSocket service = new ServerSocket(8888);//调用服务器套接字对象中的方法 accpet() ,获取客户端套接字对象 Socket socket = service.accept();//通过客户端套接字对象 Socket,获取字节输入流,读取客户端发送来的消息 InputStream in = socket.getInputStream();byte[] data = new byte[1024];int len = in.read(data);System.out.println(new String(data,0,len));
//服务器向客户端回数据,字节输出流,通过客户端套接字对象获取字节输出流 OutputStream out = socket.getOutputStream();out.write("收到,谢谢!!".getBytes());
socket.close();service.close();}}
五、TCP 图片上传案例
1. TCP 上传客户端
实现步骤:
1.Socket 套接字连接服务器 2.通过 Socket 获取字节输出流,写图片 3.使用自己的流对象,读取图片数据源(FileInputStream、缓冲流)4.读取图片,使用字节输出流,将图片写到服务器(采用字节数组进行缓冲)5.通过 Socket 套接字获取字节输入流,读取服务器发回来的上传成功 6.关闭资源
public class TCPClient {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1",8000);//获取字节输出流,将图片写到服务器 OutputStream out = socket.getOutputStream();//创建字节输入流,读取本机上的数据源图片 FileInputStream fis = new FileInputStream("D:\test.jpg");//开始读写字节数组 int len = 0;byte[] bytes = new byte[1024];while((len = fis.read(bytes)) != -1){out.write(bytes,0,len);}
//给服务器写终止序列,向服务端写入一个结束标志 socket.shutdownOutput();
//获取字节输入流,读取服务器的"上传成功"InputStream in = socket.getInputStream();
len = in.read(bytes); //复用 byte 数组 System.out.println(new String(bytes,0,len));
fis.close();socket.close();}}
2. TCP 上传服务器
1.ServerSocket 套接字对象,监听端口 80002.方法 accept()获取客户端的连接对象 3.客户端连接对象获取字节输入流,读取客户端发送图片 4.创建 File 对象,绑定上传文件夹(判断文件夹存在,不存在,创建文件夹)5.创建字节输出流,数据目的 File 对象所在文件夹 6.字节流读取图片,字节流将图片写入到目的文件夹中 7.将上传成功会写客户端 8.关闭资源
public class TCPServer {public static void main(String[] args) throws IOException {ServerSocket server = new ServerSocket(8000);Socket socket = server.accept();//通过客户端连接对象,获取字节输入流,读取客户端图片 InputStream in = socket.getInputStream();//将目的文件夹封装到 File 对象 File upload = new File("E:\upload");if(! upload.exists()){upload.mkdirs();}
//防止文件同名被覆盖,重新定义文件名字//规则: 域名+当前毫秒值+6 位随机数 String filename="wangdao"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";
//创建字节输出流,将图片写入到目的文件夹中 FileOutputStream fos = new FileOutputStream(upload+File.separator+filename);//读写字节数组 byte[] bytes = new byte[1024];int len = 0;while((len = in.read(bytes)) != -1){fos.write(bytes,0,len);}
//通过客户端连接对象获取字节输出流//将"上传成功"写回客户端 socket.getOutputStream().write("上传成功!".getBytes());
fos.close();socket.close();server.close();}}
六、多线程上传案例
1. 客户端(不变)
public class TCPClient {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1",8000);//获取字节输出流,将图片写到服务器 OutputStream out = socket.getOutputStream();//创建字节输入流,读取本机上的数据源图片 FileInputStream fis = new FileInputStream("D:\test.jpg");//开始读写字节数组 int len = 0;byte[] bytes = new byte[1024];while((len = fis.read(bytes)) != -1){out.write(bytes,0,len);}
//给服务器写终止序列,向服务端写入一个结束标志 socket.shutdownOutput();
//获取字节输入流,读取服务器的"上传成功"InputStream in = socket.getInputStream();
len = in.read(bytes); //复用 byte 数组 System.out.println(new String(bytes,0,len));
fis.close();socket.close();}}
2. 创建 Upload,实现 Runnable
public class Upload implements Runnable {private Socket socket;//传递 socketpublic Upload(Socket socket){this.socket = socket;}
评论