socket 网络编程
写在前面
-
socket 是默认阻塞的,可以把他作为 位于网络层和传输层的之间的 一个抽象层
案例一、UDP 通信
-
UDP 是面向无连接的 并且传送的是数据包 利用io 流 传输数据。将其封装成包
1.发送端
-
创建发送/接受 对象 datagramsocket,用于发送数据 (创建的对象的空参的)
-
定义要发送的 数据 以字符串的形式给出
-
将字符串 转成byte 数组 (封包对象的构造方法里面是的参数是byte[])
-
将流过来的数据封装成包(datagrampacket)满参构造,指定byte[] 和byte数组的长度,IP地址对象,端口号
-
socket 对象 调用send()方法,传入封装的数据包对象 packet 进行发包
-
关流
package com.itheima.demo1udp; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class Client { public static void main(String[] args) throws Exception { // 创建datagramsocket 对象 用发送数据 DatagramSocket socket = new DatagramSocket(); // 定义要发生的数据 String str = "Hello World"; // 将数据转成byte 数组 byte[] bys = str.getBytes(); // 创建数据包对象,进行封包 udp 面向无连接 需要指定端口号 DatagramPacket packet = new DatagramPacket(bys,bys.length, InetAddress.getLocalHost(),6666); // 发送数据包 socket.send(packet); // 关流 socket.close(); } }
接收端:
-
创建 发送/接受 数据对象 (datagramsocket)作为 udp 的接受端要指定 端口号
-
创建byte[ ]数组,用于存放的 发送端发发送的数据 (因为封包的构造方法里面是byte[])
-
将发送方发来的数据 封装成包(datagrampakcet)构造方法的参数是bys 和bys.length
-
调用socket方法 接受数据包
-
打印数据(数据存放在byte[] 中,数组的长度=数据的长度=packet.getlength)
-
new String(bys,0,length)== new一个String类型的对象 取值是从第0个 长度为数组的长度 取的是bys数组
package com.itheima.demo1udp; import java.net.DatagramPacket; import java.net.DatagramSocket; public class Server { public static void main(String[] args) throws Exception { // 创建接受包数据对象,udp 面向无连接 指定端口 DatagramSocket socket = new DatagramSocket(6666); // 创建byte[]数组 用于存放数据 byte[] bys = new byte[1000]; // 创建数据包对象 用于将数据流 封装成包 DatagramPacket packet = new DatagramPacket(bys,bys.length); // 接受数据包 socket.receive(packet); // 打印数据 返回返回将要发送或接收到的数据的长度。 int length = packet.getLength(); System.out.println("接受到的资源: " + new String(bys,0,length)); // 释放资源 socket.close(); } }
案例二、 tcp 协议的使用 发送端 发送数据给 接收端
写在前面
-
tcp 之间的通信 其实就是 两个socket 之间的通信
-
socket类 一个该类的对象就是代表一个发送端
-
构造方法: Public socket (String host,int port);
==注意 该方法的执行就会连接指定的服务器,若没有异常 则连接成功(三次握手成功)
-
常用方法
-
OutputStream getOutputStream( )
-
InputStream getInputStream( )
-
-
1.发送端
-
创建socket对象,指定服务器的ip地址和端口号
-
创建字节输出流对象,关联连接通道
-
使用字节流对象将 数据写到 连接通道里去
-
释放资源
package com.itheima.demo2tcpo1; import java.io.OutputStream; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception { // 创建socket对象,指定服务器的ip地址和端口号 Socket socket = new Socket("127.0.0.1",6666); System.out.println(socket); String str = "你好吗?"; // 创建字节输出流对象,关联连接通道 OutputStream os = socket.getOutputStream(); // 使用字节流对象将 数据写到 连接通道里去 os.write(str.getBytes()); // 释放资源 socket.close(); } }
2.接受端
-
创建ServerSocket对象,指定服务器的端口号
-
调用ServerSocket对象的 accept 方法 获取到Socket对象
-
通过接收端的 socket对象 调用输入流方法
-
使用字节输入流 读取 关联通道里的数据
-
创建数组 因为关联通道在写的时候 构造方法指定值存在数组中里的
-
获得 读取的字符长度
-
打印 将字符强转成字符串
-
释放资源
package com.itheima.demo2tcpo1; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws Exception { // 创建ServerSocket对象,指定服务器的端口号 ServerSocket serverSocket = new ServerSocket(6666); // 调用ServerSocket对象的 accept 方法 获取到Socket对象 Socket socket = serverSocket.accept(); System.out.println(socket); // 通过接收端的 socket对象 调用输入流方法 InputStream is = socket.getInputStream(); // 使用字节输入流 读取 关联通道里的数据 // 创建数组 因为关联通道在写的时候 构造方法指定值存在数组中里的 byte[] bys = new byte[8192]; // 获得 读取的字符长度 int len = is.read(bys); // 打印 将字符强转成字符串 System.out.println(new String(bys,0,len)); // 释放资源 socket.close(); } }
案例三、拓展 接受方 回话给发送端
接受端(额外增加的功能)
-
在关闭之间 搞个输出流
-
将 要回复的字符串 写到数组中去(已经关联了通道)
// 回话 String str = "我很好你呢?"; socket.getOutputStream().write(str.getBytes()); // 释放资源 socket.close();
发送端(额外增加的代码)
-
在关闭资源之前去 读取 接受端写入在数组中的数据
-
打印(byte[] 强转成字符串)
// 从字节输入流中读取 byte[] bys = new byte[8192]; // 读取 int len = socket.getInputStream().read(bys); // 打印 System.out.println(new String(bys,0,len)); // 释放资源 socket.close();
案例四:对话内容不限 对话次数不限(但问一次只能回答一次)
发送端和接受端:
-
在连接建立之后 进入循环
-
不能释放资源
-
对话 内容键盘录入
注意,发送方在创建socket 对象时候 回自动进行三次握手
接受方 在调用accept( ) 返回 socket 对象时,服务器回一直处于阻塞状态 直到发送方连接进来
案例四,文件的上传
客服端 借助(输入流) 将文件从 硬盘写道 程序A里 ,程序A 借助文件(输出流)将文件输出 (在借助输入流)将文件 写道到关联通道里
服务器端,从关联通道中 借助(输出流)读取文件到 本地
客服端
-
创建Socket 对象,指定要连接的服务器的ip地址和端口号
-
创建文件输入流对象(读),关联数据源文件路径
-
通过socket获得字节输出流对象,关联连接通道(输出就是要写 在 循环读中 用到写 写就得有容器 存放 byte数组)
-
定义byte[ ] 数组,用来存储读取到得字节数据 (下一步就是常规的套路,在读循环中 写)
-
定义int 类型变量,记录读取到 字符对应的 十进制数(字节)
-
循环读(不为-1)
-
注意 while ((b=fis.read(bys))!=-1) 从此输入流中将最多
b.length
个字节的数据读入一个 byte 数组中,判断得是,字节数组中是否还有元素 -
写字节数据到连接通道中去 ==一气呵成write(bys,0,int b);
-
释放资源
package com.itheima.demo3fileup; import java.io.FileInputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception { // 创建socket对象,指定要连接服务器的IP地址和端口 Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(),6667); // 创建文件字节输入流对象,关联数据源文件文件 FileInputStream fis = new FileInputStream("day12-code\\aaa\\jobs.jpg"); // 通过Socket获得文件输出流对象,这样输出流对象就也关联了连接通道 OutputStream os = socket.getOutputStream(); // 定义byte[] 数组 用来存储读取到的字节数 byte[] bys = new byte[8192]; // 创建int 类型的变量 用来记录读出来的字符 对应的 int b; // 对文件输出流循环读 while ((b=fis.read(bys))!=-1) { // 将读出来的字节写到 数组中 去实现关联流通道 os.write(bys,0,b); } // 释放资源 socket.close(); } }
服务器端
-
创建ServeSocket 对象,绑定端口号
-
调用accept 方法 ,让服务器处于阻塞状态,接受客户端的请求,建立连接,得到socket对象
-
(上一步中得到的 socket对象)通过socket对象,会的字节输入流(读),关联连接通道
-
创建文件输出流对象 关联文件目的路径(下一步就是常规的套路,在读循环中 写)
-
定义int 类型变量,记录读取到 字符对应的 十进制数(字节)循环读(不为-1)
-
写字节数据到连接通道中去 ==一气呵成write(bys,0,int b);
-
释放资源
package com.itheima.demo3fileup; import java.io.FileOutputStream; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws Exception { // 创建ServersSocket对象,绑定端口号 ServerSocket ss = new ServerSocket(6667); // 调用accept方法,接受请求,建立连接,得到Socket对象 Socket socket = ss.accept(); // 通过socket对象 获得字节文件输入流,关联连接通道 InputStream is = socket.getInputStream(); // 创建文件字节输出流对象,关联目的文件路径 FileOutputStream fos = new FileOutputStream("day12-code\\bbb\\jobs1.jpg"); // 定义byte[],用来存放读取到的字节数据 byte[] bys = new byte[8192]; // 循环读写数据 int b; while ((b=is.read(bys))!=-1) { // 写 fos.write(bys,0,b); } // 释放资源 socket.close(); } }
文件上传的总结
-
客户端设备
-
先创建socket 对象 没话说 确定服务器的ip 和port
-
cpu 将内存的文件 读到内存中去(用输入流)
-
通过socket 对象 获取到 输出流对象(这样输出流就关联了 连接通道)
-
定义byte [ ] 、int 变量、实现读循环
-
在读循环中 将读出来的数据 写到byte[ ] 中去(这样服务器端 读 byte[ ]的时候j就关联了 连接通道 )
-
释放资源
-
-
服务器端
-
创建ServeSocket 对象,绑定端口号(没得说)
-
调用accept 方法 ,让服务器处于阻塞状态,接受客户端的请求,建立连接,得到socket对象(没得说)
-
通过socket对象 获得字节文件输入流(要读byte[ ]),关联连接通道
-
创建文件字节输出流对象,关联目的文件路径
-
定义byte [ ] 、int 变量、实现读循环
-
在读循环中 将读出来的数据 写到byte[ ] 中去
-
案例五,文件上传2.0
-
新增 上传后的 文件名自动生成
-
服务器可以接受 上传文件
-
加入线程,可以使多个客户端上传文件
客服端1
package com.itheima.demo5fileuplusplus; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; public class Client1 { public static void main(String[] args) throws Exception { // 创建socket 对象,指定服务器的 ip 和 port Socket socket = new Socket("127.0.0.1",6668); // 创建文件字节输入流 管关联数据源路径 FileInputStream fis = new FileInputStream("day12-code\\aaa\\11.jpg"); // 获取字节输出流对象,关联连接通道 OutputStream os = socket.getOutputStream(); // 定义byte数组 byte[] bys = new byte[8192]; // 定义int 类型变量 int b; /* 循环读 */ while((b=fis.read(bys))!=-1) { // 写 os.write(bys,0,b); } // 通知服务器 已经读完 socket.shutdownOutput(); // 读取 服务器的回话 // 读取 服务器的回话 // 再次管关联 连接通道 InputStream aIs = socket.getInputStream(); int ab; while ((ab = aIs.read(bys))!=-1) { System.out.println(new String(bys,0,ab)); } socket.close(); } }
客户端2
package com.itheima.demo5fileuplusplus; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; public class Client2 { public static void main(String[] args) throws Exception { // 创建socket 对象,指定服务器的 ip 和 port Socket socket = new Socket("127.0.0.1",6668); // 创建文件字节输入流 管关联数据源路径 FileInputStream fis = new FileInputStream("day12-code\\aaa\\jobs.jpg"); // 获取字节输出流对象,关联连接通道 OutputStream os = socket.getOutputStream(); // 定义byte数组 byte[] bys = new byte[8192]; // 定义int 类型变量 int b; /* 循环读 */ while((b=fis.read(bys))!=-1) { // 写 os.write(bys,0,b); } // 通知服务器 已经读完 socket.shutdownOutput(); // 读取 服务器的回话 // 再次管关联 连接通道 InputStream aIs = socket.getInputStream(); int ab; while ((ab = aIs.read(bys))!=-1) { System.out.println(new String(bys,0,ab)); } socket.close(); } }
服务器端
package com.itheima.demo5fileuplusplus; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws Exception { // 创建serversocket 对象 ServerSocket ss = new ServerSocket(6668); // 服务器一直读 while (true) { // 获取到socket 对象 Socket socket = ss.accept(); // 创建文件输入流文件,关联连接通道 InputStream is = socket.getInputStream(); // 创建文件文件输出流文件,关联目的路径 (自动生成文件名) FileOutputStream fos = new FileOutputStream("day12code\\bbb\\"+System.currentTimeMillis()+".jpg"); byte[] bys = new byte[8192]; int b; System.out.println("服务器开始接受文件数据"); while ((b = is.read(bys))!=-1) { fos.write(bys,0,b); } System.out.println("服务器准备回写文件数据"); // 服务器回话,再次关联连接通路 OutputStream os = socket.getOutputStream(); // 回写上传成功的信息给客户端 os.write("文件上传成功".getBytes()); socket.close(); //ss.close(); os.close(); } } }
服务器端(线程版)
package com.itheima.demo5fileuplusplus; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * @author ChenY@itheima.com * @date 2022/8/6 21:05 */ public class Server { public static void main(String[] args) throws Exception { // 创建serversocket 对象 ServerSocket ss = new ServerSocket(6668); // 服务器一直读 while (true) { // 获取到socket 对象 Socket socket = ss.accept(); // 在连接完了 上线程 // 线程版 new Thread(new Runnable() { @Override public void run() { try { // 创建文件输入流文件,关联连接通道 InputStream is = socket.getInputStream(); // 创建文件文件输出流文件,关联目的路径 (自动生成文件名) FileOutputStream fos = new FileOutputStream("day12-code\\bbb\\"+System.currentTimeMillis()+".jpg"); byte[] bys = new byte[8192]; int b; System.out.println("服务器开始接受文件数据"); while ((b = is.read(bys))!=-1) { fos.write(bys,0,b); } System.out.println("服务器准备回写文件数据"); // 服务器回话,再次关联连接通路 OutputStream os = socket.getOutputStream(); // 回写上传成功的信息给客户端 os.write("文件上传成功".getBytes()); socket.close(); //ss.close(); os.close(); } catch (IOException e) { e.printStackTrace(); } finally { } } }).start(); } } }