当前位置: 首页 > news >正文

Socket详解

文章目录

Socket详解

1、基础概念

(1)概念
  • Socket位于应用层与传输层之间,作为应用程序与TCP/IP协议栈的桥梁。
  • 通过 IP地址 + 协议类型(TCP/UDP)+ 端口号 三元组唯一标识网络中的进程。
  • 在C#中通过 System.Net.Sockets 命名空间下的 Socket 类实现。
  • 计算机网络相关知识请参考:https://blog.csdn.net/liyou123456789/article/details/122731144
(2)通信协议支持
特性TCP SocketUDP Socket
连接方式面向连接(可靠传输)无连接(不可靠)
数据保证有序、不丢失可能乱序、丢失
适用场景文件传输、网页访问(HTTP)视频流、实时游戏
复杂度高(需维护连接状态)低(轻量级)
(3)应用场景
  • 即时通讯工具(如微信、QQ):基于TCP保证消息可靠送达,通过长连接实现实时双向通信。
  • Web服务器(HTTP服务):HTTP协议底层依赖TCP Socket,服务器监听80端口处理请求。
  • 物联网设备控制:设备作为Socket客户端定时上报数据,服务器远程发送指令。
  • 实时数据推送:服务端主动向客户端推送消息(如股票行情),需WebSocket等基于Socket的扩展。

2、通信流程

(1)流程图

(2)服务端
// 初始化Socket 
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 绑定端口并监听 
IPEndPoint localEP = new IPEndPoint(IPAddress.Any, 9000);
serverSocket.Bind(localEP);
serverSocket.Listen(10); // 允许10个连接排队 // 异步接受连接 
serverSocket.BeginAccept(new AsyncCallback(OnClientConnected), null);// 处理客户端连接
private void OnClientConnected(IAsyncResult ar) {Socket clientSocket = serverSocket.EndAccept(ar);// 启动新线程处理通信 Thread clientThread = new Thread(HandleClient);clientThread.Start(clientSocket);
}
(3)客户端
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 连接服务器 
clientSocket.Connect("127.0.0.1", 9000); // 异步接收数据
byte[] buffer = new byte[1024];
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnDataReceived), buffer);
(4)关键技术
  • 异步通信模型
    • 原理:使用 BeginAccept/BeginReceive 避免阻塞主线程,通过回调函数处理事件。
    • 优势:支持高并发,适用于服务端处理多客户端请求。
  • 数据边界处理
    • TCP粘包问题:需自定义协议(如消息头声明长度)。
    • UDP数据报:天然有边界,但需处理丢包和乱序。
  • 跨线程UI更新 :WinForm中必须通过 Control.Invoke 避免线程冲突:
this.Invoke((MethodInvoker)delegate {txtChatBox.Text += "收到消息: " + message;
});
(5)注意事项
  • 防止资源泄漏:必须显式关闭Socket
socket.Shutdown(SocketShutdown.Both); 
socket.Close();
  • 端口占用问题:服务端关闭后需等待2MSL(约1-4分钟)才能复用端口,可通过设置选项解决:
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
  • 数据编码与解析:网络字节序需统一(大端序),文本数据建议用JSON/Protobuf格式化。
  • 调试工具推荐
    • Wireshark:抓包分析协议细节。
    • 日志记录:关键操作(连接/断开/异常)写入日志文件。
  • 性能优化
    • 使用缓冲区池(Buffer Pool)减少内存分配开销。
    • 异步IO配合 SocketAsyncEventArgs 替代APM模型(更高性能)。

3、聊天室功能

(1)项目概述
  • net 版本:net8

(2)服务端(控制台)
using System.Net;
using System.Net.Sockets;
using System.Text;namespace Server
{internal class Program{static List<Socket> clientSockets = new List<Socket>();static readonly object lockObj = new object();static void Main(){const int PORT = 3000;Socket serverSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);// 绑定端口并监听 IPEndPoint localEP = new IPEndPoint(IPAddress.Any, PORT);serverSocket.Bind(localEP);serverSocket.Listen(10);Console.WriteLine($"服务器启动,监听端口 {PORT}...");// 异步接受客户端连接while (true){Socket clientSocket = serverSocket.Accept();lock (lockObj) clientSockets.Add(clientSocket);Console.WriteLine($"客户端接入: {clientSocket.RemoteEndPoint}");// 为每个客户端创建独立线程Thread clientThread = new Thread(() => HandleClient(clientSocket));clientThread.Start();}}static void HandleClient(Socket clientSocket){byte[] buffer = new byte[1024];try{while (true){int bytesRead = clientSocket.Receive(buffer);if (bytesRead == 0) break;  // 客户端断开连接string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到消息: {message}");// 广播消息给所有客户端BroadcastMessage(message, clientSocket);}}catch (Exception ex){Console.WriteLine($"错误: {ex.Message}");}finally{lock (lockObj) clientSockets.Remove(clientSocket);clientSocket.Close();Console.WriteLine($"客户端断开: {clientSocket.RemoteEndPoint}");}}static void BroadcastMessage(string message, Socket senderSocket){byte[] data = Encoding.UTF8.GetBytes(message);lock (lockObj){foreach (Socket client in clientSockets){if (client != senderSocket && client.Connected){try{client.Send(data);}catch { /* 忽略发送失败的客户端 */ }}}}}}
}
(3)客户端(控制台)
using System.Net.Sockets;
using System.Text;namespace Client
{internal class Program{static Socket clientSocket;static void Main(){const string SERVER_IP = "127.0.0.1";const int PORT = 3000;clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);try{clientSocket.Connect(SERVER_IP, PORT);Console.WriteLine("已连接到服务器,输入消息开始聊天 (输入exit退出)");// 启动接收线程 Thread receiveThread = new Thread(ReceiveMessages);receiveThread.IsBackground = true;receiveThread.Start();// 主线程处理输入发送 while (true){string input = Console.ReadLine();if (input.ToLower() == "exit") break;byte[] data = Encoding.UTF8.GetBytes(input);clientSocket.Send(data);}}catch (Exception ex){Console.WriteLine($"连接错误: {ex.Message}");}finally{clientSocket?.Close();}}static void ReceiveMessages(){byte[] buffer = new byte[1024];try{while (true){int bytesRead = clientSocket.Receive(buffer);if (bytesRead == 0) break;string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到: {message}");}}catch{Console.WriteLine("与服务器断开连接");}}}
}
(4)核心功能说明
  • 服务端机制
    • 使用Accept()阻塞监听客户端连接
    • 为每个客户端创建独立线程处理通信
    • 通过客户端列表clientSockets实现消息广播
    • 线程锁lockObj保证多线程安全
  • 客户端机制
    • 主线程处理用户输入和发送
    • 后台线程持续接收服务器消息
    • 使用Encoding.UTF8解决中文乱码问题
  • 广播策略
    • 遍历所有客户端Socket发送消息
    • 跳过消息发送者自身(senderSocket)
    • 异常处理避免断开客户端导致崩溃
(5)使用步骤
  • 启动服务端程序(监听3000端口)

  • 启动多个客户端程序(连接127.0.0.1:3000)

  • 在任意客户端输入消息,其他客户端将实时接收

4、大文件传输

(1)项目概述
  • 本Socket文件传输程序实现了高效、可靠的文件传输功能,包含三个核心特性:基于SHA256的分片校验机制、断点续传功能以及传输速度优化。

(2)接收端(控制台)
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;namespace FileReceiver
{internal class Program{private const int BufferSize = 64 * 1024; // 64KB 分片大小private const int Port = 4000;private static readonly string SavePath = @"D:\ReceivedFiles\";static void Main(){Directory.CreateDirectory(SavePath);// 创建监听SocketTcpListener listener = new TcpListener(IPAddress.Any, Port);listener.Start();Console.WriteLine($"文件接收服务已启动,监听端口: {Port}");while (true){// 接受客户端连接 using (TcpClient client = listener.AcceptTcpClient())using (NetworkStream stream = client.GetStream()){Console.WriteLine($"客户端已连接: {client.Client.RemoteEndPoint}");// 传输速度优化client.NoDelay = true; // 禁用Nagle算法client.ReceiveBufferSize = 64 * 1024; // 64KB缓冲区// 接收文件头信息(文件名+文件大小)byte[] headerBuffer = new byte[512];int headerSize = stream.Read(headerBuffer, 0, headerBuffer.Length);string header = Encoding.UTF8.GetString(headerBuffer, 0, headerSize);// 解析文件元数据string[] headerParts = header.Split('|');if (headerParts.Length < 3){Console.WriteLine("无效文件头格式");continue;}string fileName = headerParts[1];long fileSize = long.Parse(headerParts[2]);string filePath = Path.Combine(SavePath, fileName);// 处理断点续传请求bool isResume = false;long resumePosition = 0;if (headerParts[0] == "RESUME"){isResume = true;resumePosition = long.Parse(headerParts[3]);Console.WriteLine($"收到断点续传请求: {fileName},从 {resumePosition} 字节处继续接收");// 检查文件是否存在且大小正确if (File.Exists(filePath)){FileInfo fileInfo = new FileInfo(filePath);if (fileInfo.Length == resumePosition){// 发送确认消息byte[] confirmBytes = Encoding.UTF8.GetBytes("OK");stream.Write(confirmBytes, 0, confirmBytes.Length);Console.WriteLine($"开始接收: {fileName} ({fileSize}字节),继续从 {resumePosition} 字节处接收");}else{// 文件大小不匹配,重新开始传输byte[] rejectBytes = Encoding.UTF8.GetBytes("REJECT");stream.Write(rejectBytes, 0, rejectBytes.Length);Console.WriteLine("文件大小不匹配,将重新开始传输");isResume = false;}}else{// 文件不存在,重新开始传输byte[] rejectBytes = Encoding.UTF8.GetBytes("REJECT");stream.Write(rejectBytes, 0, rejectBytes.Length);Console.WriteLine("文件不存在,将重新开始传输");isResume = false;}}else if (headerParts[0] == "FILE"){Console.WriteLine($"开始接收: {fileName} ({fileSize}字节)");}else{Console.WriteLine("无效的文件头类型");continue;}// 分片接收文件内容FileMode fileMode = isResume ? FileMode.Append : FileMode.Create;using (FileStream fs = new FileStream(filePath, fileMode))using (SHA256 sha256 = SHA256.Create()){byte[] buffer = new byte[BufferSize];long totalReceived = isResume ? resumePosition : 0;// 如果是断点续传,记录当前文件大小作为起始接收位置if (isResume){fs.Seek(0, SeekOrigin.End);}while (totalReceived < fileSize){// 读取校验和长度byte[] checksumLengthBytes = new byte[sizeof(int)];int checksumLengthRead = stream.Read(checksumLengthBytes, 0, sizeof(int));if (checksumLengthRead != sizeof(int)){Console.WriteLine("接收校验和长度失败");break;}int checksumLength = BitConverter.ToInt32(checksumLengthBytes, 0);// 读取校验和byte[] receivedChecksum = new byte[checksumLength];int checksumRead = stream.Read(receivedChecksum, 0, checksumLength);if (checksumRead != checksumLength){Console.WriteLine("接收校验和失败");break;}// 读取数据长度byte[] dataLengthBytes = new byte[sizeof(int)];int dataLengthRead = stream.Read(dataLengthBytes, 0, sizeof(int));if (dataLengthRead != sizeof(int)){Console.WriteLine("接收数据长度失败");break;}int dataLength = BitConverter.ToInt32(dataLengthBytes, 0);// 读取实际数据int bytesRead = stream.Read(buffer, 0, dataLength);if (bytesRead != dataLength){Console.WriteLine("接收数据失败");break;}// 验证校验和byte[] computedChecksum = sha256.ComputeHash(buffer, 0, bytesRead);bool checksumMatch = receivedChecksum.SequenceEqual(computedChecksum);if (!checksumMatch){Console.WriteLine("校验和不匹配,传输错误");break;}// 写入文件fs.Write(buffer, 0, bytesRead);totalReceived += bytesRead;// 显示进度 Console.Write($"\r进度: {totalReceived * 100 / fileSize}%");}fs.Flush();}Console.WriteLine($"\n文件接收完成: {filePath}");}}}}
}
(3)发送端(控制台)
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;namespace FileSender
{internal class Program{private const int BufferSize = 64 * 1024; // 64KB 分片大小private const int Port = 4000;private const string ServerIP = "127.0.0.1";static void Main(){string filePath = "test_file.txt";if (!File.Exists(filePath)){Console.WriteLine("文件不存在");return;}Console.WriteLine($"使用测试文件: {filePath}");FileInfo fileInfo = new FileInfo(filePath);string fileName = fileInfo.Name;long fileSize = fileInfo.Length;// 计算分片数量int totalChunks = (int)Math.Ceiling((double)fileSize / BufferSize);try{using (TcpClient client = new TcpClient(ServerIP, Port))using (NetworkStream stream = client.GetStream()){// 传输速度优化client.NoDelay = true; // 禁用Nagle算法client.SendBufferSize = 64 * 1024; // 64KB缓冲区// 检查是否需要断点续传string tempFilePath = Path.Combine(Path.GetTempPath(), $"{fileName}.temp");long startPosition = 0;if (File.Exists(tempFilePath)){// 读取上次传输的位置using (BinaryReader reader = new BinaryReader(File.OpenRead(tempFilePath))){startPosition = reader.ReadInt64();}if (startPosition < fileSize){Console.WriteLine($"检测到未完成的传输,将从 {startPosition} 字节处继续传输");// 发送断点续传请求string resumeHeader = $"RESUME|{fileName}|{fileSize}|{startPosition}";byte[] resumeHeaderBytes = Encoding.UTF8.GetBytes(resumeHeader);stream.Write(resumeHeaderBytes, 0, resumeHeaderBytes.Length);// 等待服务器确认byte[] confirmBuffer = new byte[10];stream.Read(confirmBuffer, 0, confirmBuffer.Length);string confirm = Encoding.UTF8.GetString(confirmBuffer).Trim();if (confirm != "OK"){Console.WriteLine("服务器不支持断点续传,将重新开始传输");startPosition = 0;}}else{Console.WriteLine("文件已传输完成,无需再次传输");return;}}// 如果不是断点续传,则发送完整的文件头if (startPosition == 0){string header = $"FILE|{fileName}|{fileSize}";byte[] headerBytes = Encoding.UTF8.GetBytes(header);stream.Write(headerBytes, 0, headerBytes.Length);}Console.WriteLine($"开始发送: {fileName} ({fileSize}字节)");// 分片发送文件内容using (FileStream fs = File.OpenRead(filePath))using (SHA256 sha256 = SHA256.Create()){// 如果是断点续传,跳转到上次传输的位置if (startPosition > 0){fs.Seek(startPosition, SeekOrigin.Begin);}byte[] buffer = new byte[BufferSize];int bytesRead;long totalSent = startPosition;// 创建临时文件记录传输进度using (BinaryWriter progressWriter = new BinaryWriter(File.Open(tempFilePath, FileMode.Create))){while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0){// 增加分片校验机制byte[] checksum = sha256.ComputeHash(buffer, 0, bytesRead);stream.Write(BitConverter.GetBytes(checksum.Length), 0, sizeof(int));stream.Write(checksum, 0, checksum.Length);stream.Write(BitConverter.GetBytes(bytesRead), 0, sizeof(int));stream.Write(buffer, 0, bytesRead);totalSent += bytesRead;// 保存传输进度progressWriter.Seek(0, SeekOrigin.Begin);progressWriter.Write(totalSent);progressWriter.Flush();// 显示进度 Console.Write($"\r进度: {totalSent * 100 / fileSize}%");}stream.Flush();}// 传输完成后删除临时文件if (totalSent == fileSize && File.Exists(tempFilePath)){File.Delete(tempFilePath);}}Console.WriteLine("\n文件发送完成");}}catch (Exception ex){Console.WriteLine($"传输错误: {ex.Message}");}}}
}
(4)核心功能说明
  • 分片校验机制
    • 功能说明:使用SHA256加密算法对每个数据分片进行校验,确保文件传输的完整性和可靠性。
    • 发送端(FileSender)实现
      • 在发送每个数据分片前,使用SHA256算法计算该分片的校验和
      • 按照固定格式发送数据:先发送校验和长度,然后是校验和本身,接着是数据长度,最后是实际数据内容
      • 使用64KB作为分片大小,既保证了传输效率,也确保了校验准确性
    • 接收端(FileReceiver)实现
      • 按照发送端定义的格式接收数据:先接收校验和长度,然后接收校验和,接着接收数据长度,最后接收实际数据
      • 使用相同的SHA256算法计算接收到的数据分片的校验和
      • 将计算得到的校验和与接收到的校验和进行比对,确保数据完整性
      • 如果校验失败,可触发重传机制(当前版本未实现自动重传)
    • 代码实现关键点
// 发送端计算和发送校验和
byte[] checksum = sha256.ComputeHash(buffer, 0, bytesRead);
stream.Write(BitConverter.GetBytes(checksum.Length), 0, sizeof(int));
stream.Write(checksum, 0, checksum.Length);
stream.Write(BitConverter.GetBytes(bytesRead), 0, sizeof(int));
stream.Write(buffer, 0, bytesRead);// 接收端接收和验证校验和
// 读取校验和长度、校验和、数据长度和实际数据
// 计算接收到数据的校验和并与发送的校验和比对
  • 断点续传功能
    • 功能说明:在文件传输过程中如果发生中断(如网络故障、程序崩溃等),支持从中断位置继续传输,而无需重新开始。
    • 发送端(FileSender)实现
      • 使用临时文件记录每次传输的进度位置
      • 程序启动时检查是否存在未完成的传输任务
      • 如果存在未完成传输,发送特殊的RESUME头信息给接收端,包含文件名、总大小和期望的起始位置
      • 等待接收端确认是否可以继续传输
      • 如果接收端同意继续,从指定位置开始读取和发送文件内容
      • 传输完成后自动删除临时进度文件
    • 接收端(FileReceiver)实现
      • 解析接收到的文件头信息,区分普通传输请求(FILE)和断点续传请求(RESUME)
      • 对于断点续传请求,检查目标文件是否存在且大小与请求的起始位置匹配
      • 根据检查结果,向发送端发送确认(OK)或拒绝(REJECT)响应
      • 如果确认继续传输,使用FileMode.Append模式打开文件,并从文件末尾开始写入数据
    • 代码实现关键点
// 发送端发送断点续传请求
string tempFilePath = Path.Combine(Path.GetTempPath(), $"{fileName}.temp");
if (File.Exists(tempFilePath))
{using (BinaryReader reader = new BinaryReader(File.OpenRead(tempFilePath))){startPosition = reader.ReadInt64();}string resumeHeader = $"RESUME|{fileName}|{fileSize}|{startPosition}";// 发送断点续传请求并等待确认
}// 接收端处理断点续传请求
if (headerParts[0] == "RESUME")
{resumePosition = long.Parse(headerParts[3]);// 检查文件状态并发送确认/拒绝响应FileMode fileMode = isResume ? FileMode.Append : FileMode.Create;
}
  • 传输速度优化
    • 功能说明:通过一系列配置优化,显著提高文件传输速度。
    • 增大缓冲区大小
      • 将原有的4KB缓冲区增大到64KB,减少网络交互次数
      • 在发送端和接收端统一使用相同的缓冲区大小,确保最佳匹配
    • 禁用Nagle算法
      • Nagle算法会延迟小数据包的发送以提高吞吐量,但会增加延迟
      • 在文件传输场景中,禁用Nagle算法可以减少延迟,提高实时性
    • 优化Socket缓冲区设置
      • 显式设置发送和接收缓冲区大小为64KB,确保与分片大小匹配
      • 减少操作系统层面的缓冲区管理开销
    • 代码实现关键点
// 传输速度优化配置
client.NoDelay = true; // 禁用Nagle算法
client.SendBufferSize = 64 * 1024; // 64KB发送缓冲区
// 接收端类似设置client.ReceiveBufferSize
(5)技术架构与流程
  • 整体架构
    • 发送端(FileSender):负责读取本地文件,计算校验和,发送文件数据,并支持断点续传
    • 接收端(FileReceiver):负责监听连接,接收文件数据,验证校验和,保存文件,并支持断点续传
    • 通信协议:基于TCP协议实现可靠传输,自定义简单协议头格式
  • 传输流程
    • 连接建立阶段
      • 接收端启动并监听指定端口
      • 发送端连接到接收端
      • 双方配置Socket参数(禁用Nagle算法,设置缓冲区大小等)
    • 文件头传输阶段
      • 发送端确定使用普通传输(FILE头)还是断点续传(RESUME头)
      • 发送端发送文件头信息
      • 接收端解析文件头并做相应处理
      • 对于断点续传请求,接收端发送确认/拒绝响应
    • 文件内容传输阶段
      • 发送端以64KB为单位分片读取文件
      • 对每个分片计算SHA256校验和
      • 按顺序发送校验和长度、校验和、数据长度和数据内容
      • 接收端接收并验证每个分片
      • 发送端实时保存传输进度
    • 传输完成阶段
      • 发送端完成所有数据发送
      • 发送端删除临时进度文件
      • 双方关闭连接
(6)使用说明
  • 配置与依赖
    • 开发环境:.NET Framework/.NET Core
    • 依赖库:System.Net.Sockets、System.Security.Cryptography、System.IO
    • 端口配置:默认使用4000端口(可在代码中修改Port常量)
    • 保存路径:接收端默认保存到D:\ReceivedFiles\(可在代码中修改SavePath常量)
  • 运行方法
    • 首先启动接收端程序:
dotnet run --project FileReceiver\FileReceiver.csproj
- 然后启动发送端程序:* 发送端会自动使用项目目录下的test_file.txt作为测试文件进行传输
dotnet run --project FileSender\FileSender.csproj
(7)扩展建议
  • 实现自动重传机制,在校验失败时自动请求重传
  • 添加用户认证机制,增强安全性
  • 支持多文件并发传输
  • 添加图形用户界面,提高易用性
  • 实现传输限速功能,避免占用过多网络带宽
http://www.dtcms.com/a/531895.html

相关文章:

  • Ceph存储
  • [人工智能-大模型-87]:模型层技术 - “神经网络架构演进的全景地图”,“从简单到复杂、从单一到智能” - 通俗易懂版。
  • windows 2003 取消网站访问密码wordpress黑镜百度云盘
  • Spring Boot3零基础教程,自定义 starter,把项目封装成依赖给别人使用,笔记65
  • 建设足球网站的心得和意义渠道分销管理系统
  • 【PLC】汇川InoTouchPad在Win11上显示太小
  • OpenHarmony蓝牙技术全解析:从设备发现到数据传输的完整流程
  • 解压版MySQL的安装与卸载
  • C++编程基础(五):字符数组和字符串
  • 在线旅游网站平台有哪些山东泰安房价2023最新价格
  • [3D Max 基础知识分享]—多孔结构模型编辑
  • 【C++篇】C++11入门:踏入C++新世界的大门
  • 爬虫数据清洗可视化案例之全球灾害数据
  • QT(c++)开发自学笔记:4.Qt 3D简易实现
  • Vue3 自定义事件
  • 上海住房和城乡建设厅网站个人备案网站可以做产品推广
  • Android OpenGLES视频剪辑示例源码
  • 做淘宝客导购网站推广wordpress 明星
  • WebForms 页面
  • Leetcode 39
  • 【STM32项目开源】基于STM32的智能水质检测系统
  • 设计模式-迭代器模式(Iterator)
  • GitHub等平台形成的开源文化正在重塑天热e
  • 做网站需要用什么开发软件有哪些制作视频的软件
  • github中获得Personal Access Token
  • 从RDPDD!DrvEscape到RDPWD!ShareClass::UPSendOrders
  • RiPro数据转换CeoMax插件
  • IA复习笔记4 路由
  • 邯郸手机网站建设服务常见的网络推广工具
  • NTRU 加密系统原理及示例:NTRU、CTRU以及ITRU