一、TCP通信分析图解

  1. 【服务端】启动,创建 ServerSocket 对象,等待连接。
  2. 【客户端】启动,创建 Socket 对象,请求连接。
  3. 【服务端】接收连接,调用 accept 方法,并返回一个 Socket 对象。
  4. 【客户端】Socket 对象,获取 OutputStream,向服务端写出数据。
  5. 【服务端】Scoket 对象,获取 InputStream,读取客户端发送的数据。

    到此,客户端向服务端发送数据成功。

image.png

自此,服务端向客户端回写数据。

  1. 【服务端】Socket对象,获取OutputStream,向客户端回写数据。
  2. 【客户端】Scoket对象,获取InputStream,解析回写数据。
  3. 【客户端】释放资源,断开连接。

    二、客户端向服务端发送数据

    服务端实现

    1. public class TCPServer {
    2. public static void main(String[] args) throws IOException {
    3. //1.创建服务器ServerSocket对象和系统要指定的端口号
    4. ServerSocket server = new ServerSocket(8888);
    5. //2.使用ServerSocket对象中的方法accept,获取到请求的客户端对象Socket
    6. Socket socket = server.accept();
    7. //3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
    8. InputStream is = socket.getInputStream();
    9. //4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
    10. byte[] bytes = new byte[1024];
    11. int len = is.read(bytes);
    12. System.out.println(new String(bytes,0,len));
    13. //5.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
    14. OutputStream os = socket.getOutputStream();
    15. //6.使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
    16. os.write("收到谢谢".getBytes());
    17. //7.释放资源(Socket,ServerSocket)
    18. socket.close();
    19. server.close();
    20. }
    21. }

    客户端实现

    1. public static void main(String[] args) throws IOException {
    2. //1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
    3. Socket socket = new Socket("127.0.0.1",8888);
    4. //2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
    5. OutputStream os = socket.getOutputStream();
    6. //3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
    7. os.write("你好服务器".getBytes());
    8. //4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
    9. InputStream is = socket.getInputStream();
    10. //5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
    11. byte[] bytes = new byte[1024];
    12. int len = is.read(bytes);
    13. System.out.println(new String(bytes,0,len));
    14. //6.释放资源(Socket)
    15. socket.close();
    16. }

    三、经典案例

    1、文件上传

    客户端

    1. public class FileUpload_Client {
    2. public static void main(String[] args) throws IOException {
    3. // 1.创建流对象
    4. // 1.1 创建输入流,读取本地文件
    5. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("15-Socket/test.jpg"));
    6. // 1.2 创建输出流,写到服务端
    7. Socket socket = new Socket("localhost", 6666);
    8. BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
    9. //2.写出数据.
    10. byte[] b = new byte[1024 * 8];
    11. int len;
    12. while ((len = bis.read(b)) != -1) {
    13. bos.write(b, 0, len);
    14. bos.flush();
    15. }
    16. System.out.println("文件发送完毕");
    17. // 3.释放资源
    18. bos.close();
    19. socket.close();
    20. bis.close();
    21. System.out.println("文件上传完毕 ");
    22. }
    23. }

    服务端

    1. public class FileUpload_Server {
    2. public static void main(String[] args) throws IOException {
    3. System.out.println("服务器 启动..... ");
    4. // 1. 创建服务端ServerSocket
    5. ServerSocket serverSocket = new ServerSocket(6666);
    6. // 2. 循环接收,建立连接
    7. while (true) {
    8. /*
    9. 3. socket对象交给子线程处理,进行读写操作
    10. Runnable接口中,只有一个run方法,使用lambda表达式简化格式
    11. */
    12. Socket accept = serverSocket.accept();
    13. new Thread(() -> {
    14. try (
    15. //3.1 获取输入流对象
    16. BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
    17. //3.2 创建输出流对象, 保存到本地 .
    18. FileOutputStream fos = new FileOutputStream("15-Socket/" + System.currentTimeMillis() + ".jpg");
    19. BufferedOutputStream bos = new BufferedOutputStream(fos);
    20. ) {
    21. // 3.3 读写数据
    22. byte[] b = new byte[1024 * 8];
    23. int len;
    24. while ((len = bis.read(b)) != -1) {
    25. bos.write(b, 0, len);
    26. }
    27. // 4.=======信息回写===========================
    28. System.out.println("back ........");
    29. OutputStream out = accept.getOutputStream();
    30. out.write("上传成功".getBytes());
    31. out.close();
    32. //================================
    33. //5. 关闭 资源
    34. bos.close();
    35. bis.close();
    36. accept.close();
    37. System.out.println("文件上传已保存");
    38. } catch (IOException e) {
    39. e.printStackTrace();
    40. }
    41. }).start();
    42. }
    43. }
    44. }

    2、模拟web服务器

    原理:浏览器将地址栏的地址传输到web服务器,web服务器根据地址去检索服务器上的文件,并将内容输出到浏览器。

    1. public class ServerDemo {
    2. public static void main(String[] args) throws IOException {
    3. ServerSocket server = new ServerSocket(8888);
    4. while (true) {
    5. Socket socket = server.accept();
    6. new Thread(new Web(socket)).start();
    7. }
    8. }
    9. static class Web implements Runnable {
    10. private Socket socket;
    11. public Web(Socket socket) {
    12. this.socket = socket;
    13. }
    14. public void run() {
    15. try {
    16. //转换流,读取浏览器请求第一行
    17. BufferedReader readWb = new
    18. BufferedReader(new InputStreamReader(socket.getInputStream()));
    19. String requst = readWb.readLine();
    20. //取出请求资源的路径
    21. String[] strArr = requst.split(" ");
    22. System.out.println(Arrays.toString(strArr));
    23. // 去掉web前面的/
    24. String path = strArr[1].substring(1);
    25. System.out.println(path);
    26. // 读取客户端请求的资源文件
    27. FileInputStream fis = new FileInputStream(path);
    28. System.out.println(fis);
    29. byte[] bytes = new byte[1024];
    30. int len = 0;
    31. //向浏览器 回写数据
    32. OutputStream out = socket.getOutputStream();
    33. // 写入HTTP协议响应头,固定写法
    34. out.write("HTTP/1.1 200 OK\r\n".getBytes());
    35. out.write("Content-Type:text/html\r\n".getBytes());
    36. // 必须要写入空行,否则浏览器不解析
    37. out.write("\r\n".getBytes());
    38. while ((len = fis.read(bytes)) != -1) {
    39. out.write(bytes, 0, len);
    40. }
    41. fis.close();
    42. out.close();
    43. readWb.close();
    44. socket.close();
    45. } catch (Exception ex) {
    46. }
    47. }
    48. }
    49. }

    image.png

    四、其他

    3-BIO