欢迎光临
我们一直在努力

四十三、网络编程(下)——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 客户端代码

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 服务端代码

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 编程注意点

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

  • 四、TCP 双向通信 💬

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

    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 客户端(发消息 + 收回复)

    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 客户端(读文件 + 上传)

    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 服务端(收文件 + 保存 + 响应)

    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 多线程服务端代码

    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 启动多个客户端测试

    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!


    本篇总结 📝

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

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

    赞(0)
    未经允许不得转载:171主机测评 » 四十三、网络编程(下)——TCP 编程与 HTTP 入门
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址