四十三、网络编程(下)——TCP 编程与 HTTP 入门

😫 痛点引入 :UDP 发出去就不管了,万一丢包怎么办?文件上传必须每字节都不能少!

TCP 协议应运而生------面向连接、可靠传输、三次握手确认!☎️

下篇手写 TCP 客户端-服务端、文件上传、多线程并发服务器,最后揭秘网页背后的 HTTP 协议!


一、TCP 协议------面向连接的「电话」☎️

1.1 回顾:UDP vs TCP

对比项 UDP(上篇) TCP(本篇)
连接性 无连接 ❌ 面向连接 ✅
可靠性 不可靠(可能丢包) 可靠(确认机制)✅
效率 较低
类比 发短信、寄信 📮 打电话 ☎️
区分 发送端 / 接收端 客户端 / 服务端

1.2 TCP 三次握手(面试必问!📝)

复制代码
客户端                            服务端
  |                                |
  | ① SYN (我想连接)              |
  | -----------------------------> |
  |                                |
  | ② SYN+ACK (可以,我也准备好了) |
  | <----------------------------- |
  |                                |
  | ③ ACK (收到,开始传数据!)        |
  | -----------------------------> |
  |                                |
  ✅ 三次握手完成,连接建立!

💡 为什么是三次? 两次可能死锁(服务端以为连上了,客户端其实没收到确认),三次才能保证双方都说清楚!


二、TCP 核心类 🔧

2.1 两个套接字

类名 角色 作用 获取方式
Socket 客户端套接字 连接服务端、发送/接收数据 new Socket(ip, port)
ServerSocket 服务端套接字 监听端口、接收客户端连接 new ServerSocket(port)

2.2 核心方法速查

Socket 常用方法

方法 功能
getInputStream() 获取输入流,读取对方发来的数据 📥
getOutputStream() 获取输出流,向对方发送数据 📤
shutdownOutput() 关闭输出流(发送结束标记)⚠️
close() 关闭连接

ServerSocket 常用方法

方法 功能
accept() 接收客户端连接,返回客户端 Socket(阻塞

2.3 数据交互方式

复制代码
客户端发送 → 服务端读取:
  客户端:getOutputStream().write(...)
  服务端:getInputStream().read(...)

客户端读取 ← 服务端发送:
  客户端:getInputStream().read(...)
  服务端:getOutputStream().write(...)

三、TCP 基本通信 💬

3.1 客户端代码

java 复制代码
import java.net.Socket;
import java.io.OutputStream;

public class TCP_Client {
    public static void main(String[] args) throws Exception {
        // ⚠️ new Socket() 就会触发三次握手!
        // 成功说明连接建立 ✅,失败抛出异常
        Socket s = new Socket("192.168.26.23", 8888);

        // 发送消息给服务端
        OutputStream os = s.getOutputStream();
        os.write("江总你好!".getBytes());

        s.close();
        System.out.println("客户端发送完成!☎️");
    }
}

3.2 服务端代码

java 复制代码
import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;

public class TCP_Server {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动,等待连接...📞");

        // 1. 创建服务端,指定端口
        ServerSocket ss = new ServerSocket(8888);

        // 2. accept() 阻塞等待客户端连接
        Socket client = ss.accept();
        System.out.println("客户端已连接:" + client.getInetAddress());

        // 3. 读取客户端消息
        InputStream is = client.getInputStream();
        byte[] buf = new byte[1024];
        int len = is.read(buf);  // read() 阻塞,直到读完
        String msg = new String(buf, 0, len);
        System.out.println("收到:" + msg);

        client.close();
    }
}

3.3 ⚠️ TCP 编程注意点

  1. 服务端必须先启动!否则客户端连接失败
  2. new Socket() 触发三次握手,服务端没启动就抛异常
  3. accept() 阻塞,直到有客户端连接
  4. read() 阻塞,直到读到数据或对方关闭流

四、TCP 双向通信 💬

4.1 服务端(收消息 + 回复)

java 复制代码
import java.net.*;
import java.io.*;
import java.util.Scanner;

public class TCP_ServerPro {
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(8888);
        Scanner sc = new Scanner(System.in);
        System.out.println("服务端启动...📞");

        Socket client = ss.accept();
        System.out.println("客户端连接:" + client.getInetAddress());

        while (true) {
            // 1. 读取客户端消息
            InputStream is = client.getInputStream();
            byte[] buf = new byte[1024];
            int len = is.read(buf);
            String msg = new String(buf, 0, len);
            System.out.println("客户端:" + msg);

            // 2. 回复客户端
            System.out.print("请输入回复:");
            String reply = sc.next();
            OutputStream os = client.getOutputStream();
            os.write(reply.getBytes());
        }
    }
}

4.2 客户端(发消息 + 收回复)

java 复制代码
import java.net.*;
import java.io.*;
import java.util.Scanner;

public class TCP_ClientPro {
    public static void main(String[] args) throws Exception {
        Socket s = new Socket("192.168.26.23", 8888);
        Scanner sc = new Scanner(System.in);

        while (true) {
            // 1. 发送消息
            System.out.print("请输入消息:");
            String msg = sc.next();
            OutputStream os = s.getOutputStream();
            os.write(msg.getBytes());

            // 2. 接收服务端回复
            InputStream is = s.getInputStream();
            byte[] buf = new byte[1024];
            int len = is.read(buf);
            System.out.println("服务端回复:" + new String(buf, 0, len));
        }
    }
}

五、TCP 文件上传 📤

5.1 需求

客户端上传图片到服务端,服务端保存后给客户端响应。

5.2 客户端(读文件 + 上传)

java 复制代码
import java.net.*;
import java.io.*;

public class TCP_FileClient {
    public static void main(String[] args) throws Exception {
        Socket s = new Socket("127.0.0.1", 9999);

        // 1. 读取本地文件
        FileInputStream fis = new FileInputStream("D:/1.jpg");
        OutputStream os = s.getOutputStream();

        // 2. 循环写出(上传)文件数据
        byte[] buf = new byte[1024];
        int len;
        while ((len = fis.read(buf)) != -1) {
            os.write(buf, 0, len);
        }

        // ⚠️ 关键!告诉服务端"我传完了"
        s.shutdownOutput();

        fis.close();

        // 3. 读取服务端响应
        InputStream is = s.getInputStream();
        byte[] respBuf = new byte[1024];
        int respLen = is.read(respBuf);
        System.out.println("服务端:" + new String(respBuf, 0, respLen));

        s.close();
    }
}

5.3 服务端(收文件 + 保存 + 响应)

java 复制代码
import java.net.*;
import java.io.*;
import java.util.Random;

public class TCP_FileServer {
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(9999);
        System.out.println("文件服务器启动...📤");

        Socket client = ss.accept();
        System.out.println("客户端上传:" + client.getInetAddress());

        // 1. 读取客户端上传数据
        InputStream is = client.getInputStream();

        // 2. 生成随机文件名(防止覆盖)
        Random r = new Random();
        FileOutputStream fos = new FileOutputStream(
            "D:/upload/" + r.nextInt(Integer.MAX_VALUE) + ".jpg"
        );

        byte[] buf = new byte[1024];
        int len;
        while ((len = is.read(buf)) != -1) {
            fos.write(buf, 0, len);  // 保存到磁盘
        }
        fos.close();

        // 3. 给客户端响应
        OutputStream os = client.getOutputStream();
        os.write("上传成功!✅".getBytes());

        client.close();
    }
}

5.4 ⚠️ shutdownOutput() ------ 文件上传的灵魂

复制代码
不用 shutdownOutput():
  服务端 read() 永远阻塞,不知道客户端传完了 😱

用了 shutdownOutput():
  客户端调用后,发送一个"结束标记"
  服务端 read() 收到 -1,跳出循环 ✅

一句话shutdownOutput() = 告诉对方"我说完了,你可以处理了"!


六、TCP 多线程并发服务器 🧵

6.1 为什么需要多线程?

复制代码
单线程服务器:
  客户端A 连接 → 服务器处理A → 处理完才能处理B
  → 客户端B 等着,体验极差 ❌

多线程服务器:
  客户端A 连接 → 开线程1 处理A
  客户端B 连接 → 开线程2 处理B
  → 同时处理,互不影响 ✅

6.2 多线程服务端代码

java 复制代码
import java.net.*;
import java.io.*;
import java.util.Random;

public class TCP_MultiThreadServer {
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(9999);
        System.out.println("多线程服务器启动...🧵");

        while (true) {
            Socket client = ss.accept();  // 等待客户端
            System.out.println("新客户端:" + client.getInetAddress());

            // 为每个客户端开启独立线程!
            new Thread(() -> {
                try {
                    // 接收文件
                    InputStream is = client.getInputStream();
                    Random r = new Random();
                    FileOutputStream fos = new FileOutputStream(
                        "D:/upload/" + r.nextInt(Integer.MAX_VALUE) + ".jpg"
                    );

                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    fos.close();

                    // 响应客户端
                    OutputStream os = client.getOutputStream();
                    os.write("上传成功!✅".getBytes());

                    client.close();
                    System.out.println("客户端上传完成!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();  // 启动线程!
        }
    }
}

6.3 启动多个客户端测试

java 复制代码
public class TCP_MultiClientTest {
    public static void main(String[] args) {
        // 同时启动 3 个客户端,并发上传
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    Socket s = new Socket("127.0.0.1", 9999);
                    FileInputStream fis = new FileInputStream("D:/1.jpg");
                    OutputStream os = s.getOutputStream();

                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = fis.read(buf)) != -1) {
                        os.write(buf, 0, len);
                    }
                    fis.close();
                    s.shutdownOutput();

                    // 接收响应
                    InputStream is = s.getInputStream();
                    byte[] resp = new byte[1024];
                    int respLen = is.read(resp);
                    System.out.println(Thread.currentThread().getName()
                        + ":" + new String(resp, 0, respLen));
                    s.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

💡 多线程上传优势

  • 多个客户端同时上传
  • 每个客户端独占线程,互不干扰
  • 服务端持续运行,不用重启

七、HTTP 协议入门------网页背后的原理 🌐

7.1 HTTP 是什么

HTTP(HyperText Transfer Protocol):超文本传输协议,应用层最常用的协议。

7.2 HTTP 请求格式

复制代码
GET /index.html HTTP/1.1        ← 请求行(方法 + 路径 + 版本)
Host: www.example.com         ← 请求头
User-Agent: Mozilla/5.0
                         ← 空行(必须!)
[请求体]                  ← GET 请求通常没有

7.3 HTTP 响应格式

复制代码
HTTP/1.1 200 OK              ← 状态行(版本 + 状态码 + 消息)
Content-Type: text/html    ← 响应头
Content-Length: 1234
                         ← 空行(必须!)
<html>...</html>           ← 响应体(网页内容)

7.4 常见状态码

状态码 含义
200 请求成功 ✅
301 永久重定向
302 临时重定向
404 资源未找到 ❌
500 服务器内部错误 ⚠️

7.5 💡 TCP 与 HTTP 的关系

复制代码
TCP 是传输层协议 → 负责可靠传输数据 ☎️
  ↓
HTTP 是应用层协议 → 定义数据格式(请求头/响应头)🌐
  ↓
HTTP 底层使用 TCP 传输!

一句话:HTTP = 带格式的 TCP!


本篇总结 📝

  1. TCP 协议 ☎️:面向连接、可靠传输、三次握手确认
  2. 三次握手 📝:SYN → SYN+ACK → ACK,保证双方都确认连接
  3. Socket vs ServerSocket 🔧:客户端 new Socket(ip,port)、服务端 new ServerSocket(port) + accept()
  4. 数据交互 📥📤:getInputStream() 读、getOutputStream()
  5. TCP 双向通信 💬:客户端发→服务端收→服务端回→客户端收
  6. 文件上传 📤:客户端读文件写服务端 + shutdownOutput() 发送结束标记
  7. 多线程服务器 🧵:while(true) { accept(); new Thread(...).start(); } 支持并发
  8. HTTP 协议 🌐:应用层协议,定义请求/响应格式,底层用 TCP 传输

作者 :书源丶
发布平台:CSDN

相关推荐
木井巳1 小时前
【递归算法】单词搜索
java·算法·leetcode·决策树·深度优先
幸运的大号暖贴2 小时前
解决Vibe Coding时Idea经常不自动git add问题
java·人工智能·git·intellij-idea·claudecode·opencode
m0_716255002 小时前
第一部分 数据开发 面试全题 模拟口述版(自问自答)
java·数据库·面试
SuperherRo2 小时前
服务攻防-Java组件安全&FastJson&高版本JNDI&不出网C3P0&编码绕WAF&写入文件CI链
java·安全·fastjson·waf·不出网·高版本·写入文件
丑八怪大丑2 小时前
SQL数据类型
java·数据库·sql
Nyarlathotep01132 小时前
并发集合类(3):LinkedBlockingQueue
java·后端
李温候2 小时前
互联网大厂Java求职者面试全攻略
java·数据库·面试·orm·构建工具·web框架·互联网大厂
摇滚侠3 小时前
软件开发外包项目组,如何提高代码质量和开发效率
java·开发语言·前端·ide·intellij-idea