网络编程
-
TCP通信–(C/S架构)
-
TCPClient
package cn.day19.xiaoge.demo01; /* TCP通信的客户端: 向服务器发送链接请求, 给服务器发送数据, 读取服务器回写的数据 表示客户端的类: java.net.Socket: 此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。 套接字: 包含了IP地址和端口号的网络单位 构造方法: Socket(String host, int port) 创建一个流套接字并将其链接到指定主机上的指定端口号 参数: String host: 服务器主机名称/服务器的IP地址 int port: 服务器的端口号 成员方法: OutputStream getOutputStream() 返回此套接字的输出流 InputStream getInputStream() 返回此套接字的输入流 void close() 关闭套接字 实现步骤: 1. 创建一个客户端套接字对象Socket, 构造方法绑定服务器的IP地址和端口号 2. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象 3. 使用网络套接字输出流OutputStream对象中的方法write, 给服务器发送数据 4. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象 5. 使用网络套接字输入流InputStream对象中的方法read, 读取服务器会写的数据 6. 释放资源(Socket) 注意: 1. 客户端和服务器端进行交互, 必须使用Socket中提供的网络流, 不能使用字节创建的流对象 2. 当我们创建客户端对象Socket的时候, 就会去请求服务器和服务器经过3次握手建立连接服务器 这时如果服务器没有启动, 那么就会跑出异常 如果服务器已经启动, 那么就可以进行交互 */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class TCPClient { public static void main(String[] args) throws IOException { // 1. 创建一个客户端套接字对象Socket, 构造方法绑定服务器的IP地址和端口号 Socket socket = new Socket("127.0.0.1", 8888); // 2. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象 OutputStream gos = socket.getOutputStream(); // 3. 使用网络套接字输出流OutputStream对象中的方法write, 给服务器发送数据 gos.write("没意义".getBytes()); // 4. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象 InputStream gis = socket.getInputStream(); // 5. 使用网络套接字输入流InputStream对象中的方法read, 读取服务器会写的数据 byte[] bytes = new byte[1024]; int len = gis.read(bytes); System.out.println(new String(bytes, 0, len)); // 6. 释放资源(Socket) socket.close(); } }
-
TCPServer
package cn.day19.xiaoge.demo01; /* TCP通信的服务器端: 接收客户端的请求, 读取客户端发送的数据, 给客户端回写数据 表示服务器的类: java.new.ServerSocket: 此类实现服务器套接字 构造方法: ServerSocket(int port) 创建绑定到特定端口的服务器套接字 服务器端必须明确一件事情, 必须的知道是那个客户端请求的数据 所以可以使用accept方法获取到请求的客户的对象Socket 成员方法: Socket accept() 侦听并接受到此套接字的链接 服务器的实现步骤: 1. 创建服务器ServerSocket对象和系统要指定的端口号 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端对象Socket 3. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象 4. 使用网络字节输入流InputStream对象中的方法read, 读取客户端发送的数据 5. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream度夏宁 6. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写数据 7. 释放资源(Socket, ServerSocket) */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { // 1. 创建服务器ServerSocket对象和系统要指定的端口号 ServerSocket server = new ServerSocket(8888); // 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端对象Socket Socket socket = server.accept(); // 3. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象 InputStream gis = socket.getInputStream(); // 4. 使用网络字节输入流InputStream对象中的方法read, 读取客户端发送的数据 byte[] bytes = new byte[1024]; int len = gis.read(bytes); System.out.println(new String(bytes, 0, len)); // 5. 使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream度夏宁 OutputStream gos = socket.getOutputStream(); // 6. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写数据 gos.write("盘他".getBytes()); // 7. 释放资源(Socket, ServerSocket) socket.close(); server.close(); } }
-
-
文件上传(本地上传到服务器)–(C/S架构)
-
TCPClient
package cn.day19.xiaoge.demo02; /* 文件上传案例的客户端: 读取本地文件, 上传到服务器, 读取服务器会写的数据 明确: 数据源: ./1.jpg 目的地: 服务器 实现步骤: 1. 创建一个本地字节输入流FileInputStream对象, 构造方法中绑定要读取的数据源 2. 创建一个客户端Socket对象, 构造方法中绑定服务器的IP地址和端口号 3. 使用Socket对象中的方法getOutputStream, 获取网络字节输出流OutputStream对象 4. 本地字节输入流FileInputStream对象中的方法read, 读取本地文件 5. 使用网络字节输出流OutputStream对象中的write, 把读取到的文件上传到服务器 6. 使用Socket中的方法getInputStream, 获取网络字节输入流InputStream对象 7. 使用网络字节输入流InputStream对象中的方法read, 读取服务器会写的数据 8. 释放资源(FileInputStream, Socket) */ import java.io.*; import java.net.Socket; public class TCPClient { public static void main(String[] args) throws IOException { // 1. 创建一个本地字节输入流FileInputStream对象, 构造方法中绑定要读取的数据源 FileInputStream fis = new FileInputStream("/Users/xiaoge/IdeaProjects/day19-code/day19-code/1.jpg"); // 2. 创建爱你一个客户端Socket对象, 构造方法中绑定服务器的IP地址和端口号 Socket socket = new Socket("127.0.0.1", 8888); // 3. 使用Socket对象中的方法getOutputStream, 获取网络字节输出流OutputStream对象 OutputStream os = socket.getOutputStream(); // 4. 本地字节输入流FileInputStream对象中的方法read, 读取本地文件 byte[] bytes = new byte[1024]; int len = 0; while ((len = fis.read(bytes)) != -1) { // 5. 使用网络字节输出流OutputStream对象中的write, 把读取到的文件上传到服务器 os.write(bytes, 0, len); // 这里结束标记没有发送给服务器, 这样服务器变成堵塞状态 // 因为read方法没有读取到结束标记, 会进入堵塞状态 } /* 解决: 服务器堵塞问题, 上传完文件, 给服务器写一个结束标记 void shutdownOutput() 禁用此套接字的输出流 对于 TCP 套接字, 任何以前写入的数据都将发送, 并且后跟 TCP 的正常连接终止序列 */ socket.shutdownOutput(); // 6. 使用Socket中的方法getInputStream, 获取网络字节输入流InputStream对象 InputStream is = socket.getInputStream(); // 7. 使用网络字节输入流InputStream对象中的方法read, 读取服务器会写的数据 while ((len = is.read(bytes)) != -1) { System.out.println(new String(bytes, 0, len)); } // 8. 释放资源(FileInputStream, Socket) fis.close(); socket.close(); } }
-
TCPServer
package cn.day19.xiaoge.demo02; /* 文件上传案例服务器端: 读取客户端上传的文件, 保存到服务器的硬盘, 给客户端回写"上传成功" 明确: 数据源: 客户端上传的文件 目的地: 服务器的硬盘: ./upload/1.jpg 实现步骤: 1. 创建一个服务器ServerSocket对象, 和系统要指定的端口号 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端Socket对象 3. 使用Socket中的方法getInputStream, 获取网路字节输入流InputStream对象 4. 判断./upload文件夹是否存在, 不存在则创建 5. 创建一个本地字节输出流FileOutputStream对象, 构造方法中绑定要输出的目的地 6. 使用网络字节输入流InputStream对象中的方法read, 读取客户端上传的文件 7. 使用本地字节输出流FileOutputStream中的方法write, 把读取到的文件保存到服务器上 8. 使用Socket对象中的方法getOutputStream, 获取到网络字节输出流OutputStream对象 9. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写"上传成功" 10. 释放资源(FileOutputStream, Socket, ServerSocket) */ import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { // 1. 创建一个服务器ServerSocket对象, 和系统要指定的端口号 ServerSocket server = new ServerSocket(8888); // 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端Socket对象 Socket socket = server.accept(); // 3. 使用Socket中的方法getInputStream, 获取网路字节输入流InputStream对象 InputStream is = socket.getInputStream(); // 4. 判断./upload文件夹是否存在, 不存在则创建 File file = new File("./upload"); // 判断传递的路径文件夹是否存在 if (!file.exists()){ // 创建你构造方法中传递的路径的文件夹 file.mkdirs(); } // 5. 创建一个本地字节输出流FileOutputStream对象, 构造方法中绑定要输出的目的地 FileOutputStream fos = new FileOutputStream("./upload/1.jpg"); // 6. 使用网络字节输入流InputStream对象中的方法read, 读取客户端上传的文件 byte[] bytes = new byte[1024]; int len = 0; while((len = is.read(bytes)) != -1){ // 这里会接受到客户端发送的, TCP正常连接的终止序列, 所以这里(read能读取到结束标记) // 7. 使用本地字节输出流FileOutputStream中的方法write, 把读取到的文件保存到服务器上 fos.write(bytes, 0, len); } // 8. 使用Socket对象中的方法getOutputStream, 获取到网络字节输出流OutputStream对象 OutputStream os = socket.getOutputStream(); // 9. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写"上传成功" os.write("上传成功".getBytes()); // 10. 释放资源(FileOutputStream, Socket, ServerSocket) fos.close(); socket.close(); server.close(); } }
-
-
文件上传到服务器(服务器使用多线程), 读取服务器会写数据–(C/S架构)
-
TCPClient
package cn.day19.xiaoge.demo03; /* 文件上传案例的客户端: 读取本地文件, 上传到服务器, 读取服务器会写的数据 明确: 数据源: ./1.jpg 目的地: 服务器 实现步骤: 1. 创建一个本地字节输入流FileInputStream对象, 构造方法中绑定要读取的数据源 2. 创建爱你一个客户端Socket对象, 构造方法中绑定服务器的IP地址和端口号 3. 使用Socket对象中的方法getOutputStream, 获取网络字节输出流OutputStream对象 4. 本地字节输入流FileInputStream对象中的方法read, 读取本地文件 5. 使用网络字节输出流OutputStream对象中的write, 把读取到的文件上传到服务器 6. 使用Socket中的方法getInputStream, 获取网络字节输入流InputStream对象 7. 使用网络字节输入流InputStream对象中的方法read, 读取服务器会写的数据 8. 释放资源(FileInputStream, Socket) */ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class TCPClient { public static void main(String[] args) throws IOException { // 1. 创建一个本地字节输入流FileInputStream对象, 构造方法中绑定要读取的数据源 FileInputStream fis = new FileInputStream("/Users/xiaoge/IdeaProjects/day19-code/day19-code/1.jpg"); // 2. 创建爱你一个客户端Socket对象, 构造方法中绑定服务器的IP地址和端口号 Socket socket = new Socket("127.0.0.1", 8888); // 3. 使用Socket对象中的方法getOutputStream, 获取网络字节输出流OutputStream对象 OutputStream os = socket.getOutputStream(); // 4. 本地字节输入流FileInputStream对象中的方法read, 读取本地文件 byte[] bytes = new byte[1024]; int len = 0; while ((len = fis.read(bytes)) != -1) { // 5. 使用网络字节输出流OutputStream对象中的write, 把读取到的文件上传到服务器 os.write(bytes, 0, len); // 这里结束标记没有发送给服务器, 这样服务器变成堵塞状态 // 因为read方法没有读取到结束标记, 会进入堵塞状态 } /* 解决: 服务器堵塞问题, 上传完文件, 给服务器写一个结束标记 void shutdownOutput() 禁用此套接字的输出流 对于 TCP 套接字, 任何以前写入的数据都将发送, 并且后跟 TCP 的正常连接终止序列 */ socket.shutdownOutput(); // 6. 使用Socket中的方法getInputStream, 获取网络字节输入流InputStream对象 InputStream is = socket.getInputStream(); // 7. 使用网络字节输入流InputStream对象中的方法read, 读取服务器会写的数据 while ((len = is.read(bytes)) != -1) { System.out.println(new String(bytes, 0, len)); } // 8. 释放资源(FileInputStream, Socket) fis.close(); socket.close(); } }
-
TCPServer
package cn.day19.xiaoge.demo03; /* 文件上传案例服务器端: 读取客户端上传的文件, 保存到服务器的硬盘, 给客户端回写"上传成功" 明确: 数据源: 客户端上传的文件 目的地: 服务器的硬盘: ./upload/1.jpg 实现步骤: 1. 创建一个服务器ServerSocket对象, 和系统要指定的端口号 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端Socket对象 3. 使用Socket中的方法getInputStream, 获取网路字节输入流InputStream对象 4. 判断./upload文件夹是否存在, 不存在则创建 5. 创建一个本地字节输出流FileOutputStream对象, 构造方法中绑定要输出的目的地 6. 使用网络字节输入流InputStream对象中的方法read, 读取客户端上传的文件 7. 使用本地字节输出流FileOutputStream中的方法write, 把读取到的文件保存到服务器上 8. 使用Socket对象中的方法getOutputStream, 获取到网络字节输出流OutputStream对象 9. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写"上传成功" 10. 释放资源(FileOutputStream, Socket, ServerSocket) */ import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; public class TCPServer { public static void main(String[] args) throws IOException { // 1. 创建一个服务器ServerSocket对象, 和系统要指定的端口号 ServerSocket server = new ServerSocket(8888); /* 让服务器一直处于监听状态(死循环accept方法) 有一个客户端上传文件, 就保存一个文件 */ while(true) { // 2. 使用ServerSocket对象中的方法accept, 获取到请求的客户端Socket对象 Socket socket = server.accept(); /* 使用多线程技术提高程序效率 有一个客户端上传文件, 就开启一个线程, 完成文件的上传 */ new Thread(new Runnable() { @Override public void run() { try{ // 3. 使用Socket中的方法getInputStream, 获取网路字节输入流InputStream对象 InputStream is = socket.getInputStream(); // 4. 判断./upload文件夹是否存在, 不存在则创建 File file = new File("./upload"); // 判断传递的路径文件夹/文件是否存在 if (!file.exists()){ // 递归创建文件夹 file.mkdirs(); } // 打印构造方法中传递的路径 // System.out.println(file); /* 自定义一个文件的命名规则: 防止同名的文件被覆盖 规则: 域名 + 毫秒值 + 随机数 */ String fileName = "xiaoge" + System.currentTimeMillis() + new Random().nextInt(999999) + ".jpg"; // 5. 创建一个本地字节输出流FileOutputStream对象, 构造方法中绑定要输出的目的地 FileOutputStream fos = new FileOutputStream(file + "/" + fileName); // 6. 使用网络字节输入流InputStream对象中的方法read, 读取客户端上传的文件 byte[] bytes = new byte[1024]; int len = 0; while((len = is.read(bytes)) != -1){ // 这里会接受到客户端发送的, TCP正常连接的终止序列, 所以这里(read能读取到结束标记) // 7. 使用本地字节输出流FileOutputStream中的方法write, 把读取到的文件保存到服务器上 fos.write(bytes, 0, len); } // 8. 使用Socket对象中的方法getOutputStream, 获取到网络字节输出流OutputStream对象 OutputStream os = socket.getOutputStream(); // 9. 使用网络字节输出流OutputStream对象中的方法write, 给客户端会写"上传成功" os.write("上传成功".getBytes()); // 10. 释放资源(FileOutputStream, Socket, ServerSocket) fos.close(); socket.close(); }catch(IOException e) { System.out.println(e); } } }).start(); } // 服务器不用关闭 // server.close(); } }
-
-
浏览器发送数据, 服务器接收并处理(B/S架构)
-
TCPServer
package cn.day19.xiaoge.demo04; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /* 创建B/S版本TCP服务器 */ public class TCPServer { public static void main(String[] args) throws IOException { // 创建一个服务器ServerSocket, 和系统要指定的端口号 ServerSocket server = new ServerSocket(8888); // 使用accept方法获取到请求的客户端对象(浏览器) Socket socket = server.accept(); // 使用Socket对象中的方法getInputStream, 获取到网络字节输入InputStream对象 InputStream is = socket.getInputStream(); // 把is网络字节输入流对象, 转换为字符缓冲输入流 BufferedReader br = new BufferedReader(new InputStreamReader(is)); // 把客户端请求信息的第一行读取出来 GET /day19-code/web/index.html HTTP/1.1 String line = br.readLine(); // 把读取的信息进行切割, 只要中间部分 /day19-code/web/index.html String[] arr = line.split(" "); // 把路径前边的/去掉, 进行截取 day19-code/web/index.html String htmlPath = arr[1].substring(1); // 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象 OutputStream os = socket.getOutputStream(); // 创建一个本地字节输入流, 构造方法中绑定要读取的html路径 FileInputStream fis = new FileInputStream(htmlPath); // 写入HTTP协议响应头, 固定写法 os.write("HTTP/1.1 200 OK\n".getBytes()); os.write("Content-Type:text/html\n".getBytes()); // 必须要写入空行, 否则浏览器不解析 os.write("\n".getBytes()); // 一读一写赋值文件, 把服务器读取的html文件会写到客户端(浏览器) byte[] bytes = new byte[1024]; int len = 0; while((len = fis.read(bytes)) != -1){ os.write(bytes, 0, len); } } }
-
TCPServerThread–(多线程版本)
package cn.day19.xiaoge.demo04; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /* 创建B/S版本TCP服务器 */ public class TCPServerThread { public static void main(String[] args) throws IOException { // 创建一个服务器ServerSocket, 和系统要指定的端口号 ServerSocket server = new ServerSocket(8888); /* 浏览器解析服务器会写的html页面, 页面中如果有图片, 那么浏览器就会单独的开启一个线程, 读取服务器文件 我们就让服务器一直处于监听状态, 客户端请求一次, 那么服务器就会写一次 */ while(true) { // 使用accept方法获取到请求的客户端对象(浏览器) /* accept是阻塞的, 不能放在线程里面, 这样线程创建不成功, 因为start()方法执行不到 */ Socket socket = server.accept(); new Thread(new Runnable() { @Override public void run() { try{ // 使用Socket对象中的方法getInputStream, 获取到网络字节输入InputStream对象 InputStream is = socket.getInputStream(); // 把is网络字节输入流对象, 转换为字符缓冲输入流 BufferedReader br = new BufferedReader(new InputStreamReader(is)); // 把客户端请求信息的第一行读取出来 GET /day19-code/web/index.html HTTP/1.1 String line = br.readLine(); // 把读取的信息进行切割, 只要中间部分 /day19-code/web/index.html String[] arr = line.split(" "); // 把路径前边的/去掉, 进行截取 day19-code/web/index.html String htmlPath = arr[1].substring(1); // 使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象 OutputStream os = socket.getOutputStream(); // 创建一个本地字节输入流, 构造方法中绑定要读取的html路径 FileInputStream fis = new FileInputStream(htmlPath); // 写入HTTP协议响应头, 固定写法 os.write("HTTP/1.1 200 OK\n".getBytes()); os.write("Content-Type:text/html\n".getBytes()); // 必须要写入空行, 否则浏览器不解析 os.write("\n".getBytes()); // 一读一写赋值文件, 把服务器读取的html文件会写到客户端(浏览器) byte[] bytes = new byte[1024]; int len = 0; while((len = fis.read(bytes)) != -1){ os.write(bytes, 0, len); } // 释放资源 fis.close(); socket.close(); }catch(IOException e){ System.out.println(e); } } }).start(); } } }
-