三.网络编程

2026-05-12 08:01 185 阅读

c# 网络编程

1.网络编程基础概念

1.1 OSI模型与TCP/IP协议栈

  • ​应用层​​:HTTP、FTP、SMTP等协议
  • ​​传输层​​:TCP(可靠)、UDP(不可靠)
  • ​​网络层​​:IP协议
  • ​​链路层​​:以太网、Wi-Fi等

1.2 常见网络术语

  • ​​IP地址​​:标识网络设备的唯一地址(IPv4如192.168.1.1,IPv6如2001:0db8:85a3::8a2e:0370:7334)
  • ​​​​端口号​​:0-65535,其中0-1023为知名端口
  • ​​Socket​​:网络通信的端点,包含IP地址和端口号
  • ​​三次握手​​:TCP建立连接的过程
  • ​​​​四次挥手​​:TCP断开连接的过程

2.TCP编程详解

2.1 TCP服务器实现

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

class TcpServer
{
    private const int Port = 8888;
    private const int BufferSize = 1024;
    
    public static void Start()
    {
        // 1. 创建监听Socket
        IPAddress ipAddress = IPAddress.Any;
        TcpListener listener = new TcpListener(ipAddress, Port);
        
        try
        {
            // 2. 开始监听
            listener.Start();
            Console.WriteLine($"服务启动完成 {ipAddress}:{Port}");
            
            while (true)
            {
                Console.WriteLine("等待连接...");
                
                // 3. 接受客户端连接(阻塞)
                TcpClient client = listener.AcceptTcpClient();
                Console.WriteLine($"Client connected: {client.Client.RemoteEndPoint}");
                
                // 4. 为每个客户端创建独立线程
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient));
                clientThread.Start(client);
            }
        }
        catch (SocketException e)
        {
            Console.WriteLine($"SocketException: {e}");
        }
        finally
        {
            listener.Stop();
        }
    }
    
    private static void HandleClient(object obj)
    {
        TcpClient client = (TcpClient)obj;
        NetworkStream stream = client.GetStream();
        
        byte[] buffer = new byte[BufferSize];
        int bytesRead;
        
        try
        {
            // 5. 读取客户端数据
            while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
            {
                string data = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                Console.WriteLine($"Received: {data} from {client.Client.RemoteEndPoint}");
                
                // 6. 处理并返回响应
                string response = ProcessRequest(data);
                byte[] responseData = Encoding.ASCII.GetBytes(response);
                stream.Write(responseData, 0, responseData.Length);
                Console.WriteLine($"Sent: {response}");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine($"Exception: {e}");
        }
        finally
        {
            // 7. 关闭连接
            client.Close();
            Console.WriteLine($"Client disconnected: {client.Client.RemoteEndPoint}");
        }
    }
    
    private static string ProcessRequest(string request)
    {
        // 简单的请求处理逻辑
        return $"服务处理: {request.ToUpper()}";
    }
}

2.2 TCP客户端实现

using System;
using System.Net.Sockets;
using System.Text;

class TcpClientExample
{
    private const string ServerIP = "127.0.0.1";
    private const int Port = 8888;
    
    public static void Start()
    {
        try
        {
            // 1. 创建TcpClient并连接服务器
            using TcpClient client = new TcpClient(ServerIP, Port);
            Console.WriteLine($"连接到服务{ServerIP}:{Port}");
            
            NetworkStream stream = client.GetStream();
            
            while (true)
            {
                // 2. 获取用户输入
                Console.Write("回车输入 ('exit' 退出): ");
                string message = Console.ReadLine();
                
                if (message.ToLower() == "exit")
                    break;
                
                // 3. 发送数据到服务器
                byte[] data = Encoding.ASCII.GetBytes(message);
                stream.Write(data, 0, data.Length);
                Console.WriteLine($"Sent: {message}");
                
                // 4. 接收服务器响应
                byte[] buffer = new byte[1024];
                int bytesRead = stream.Read(buffer, 0, buffer.Length);
                string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                Console.WriteLine($"Received: {response}");
            }
        }
        catch (ArgumentNullException e)
        {
            Console.WriteLine($"ArgumentNullException: {e}");
        }
        catch (SocketException e)
        {
            Console.WriteLine($"SocketException: {e}");
        }
    }
}

2.3 TCP编程注意事项

1.异常处理​​:必须处理SocketException、IOException等异常 2.​​资源释放​​:使用using语句或手动关闭TcpClient和NetworkStream 3.​​多线程​​:服务器通常需要为每个客户端创建独立线程 4.​​缓冲区管理​​:合理设置缓冲区大小,处理大数据的分包传输 5.​​连接状态检测​​:通过Poll方法检测连接是否仍然有效

3.UDP编程详解

3.1 UDP服务器实现

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

class UdpServer
{
    private const int Port = 9999;
    
    public static void Start()
    {
        // 1. 创建UdpClient
        UdpClient server = new UdpClient(Port);
        IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
        
        Console.WriteLine($"服务器启动完成,端口:{Port}");
        
        try
        {
            while (true)
            {
                // 2. 接收数据
                byte[] receivedData = server.Receive(ref remoteEP);
                string message = Encoding.ASCII.GetString(receivedData);
                
                Console.WriteLine($"接收到 {remoteEP}: {message}");
                
                // 3. 处理并返回响应
                string response = $"服务器已接收: {message}";
                byte[] responseData = Encoding.ASCII.GetBytes(response);
                
                // 4. 发送响应
                server.Send(responseData, responseData.Length, remoteEP);
                Console.WriteLine($"回复:{remoteEP}");
            }
        }
        catch (SocketException e)
        {
            Console.WriteLine($"SocketException: {e}");
        }
        finally
        {
            server.Close();
        }
    }
}

3.2 UDP客户端实现

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

class UdpClientExample
{
    private const string ServerIP = "127.0.0.1";
    private const int ServerPort = 9999;
    private const int LocalPort = 9998;
    
    public static void Start()
    {
        // 1. 创建UdpClient并绑定本地端口
        UdpClient client = new UdpClient(LocalPort);
        IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse(ServerIP), ServerPort);
        
        try
        {
            while (true)
            {
                // 2. 获取用户输入
                Console.Write("回车输入 ('exit' 退出): ");
                string message = Console.ReadLine();
                
                if (message.ToLower() == "exit")
                    break;
                
                // 3. 发送数据到服务器
                byte[] data = Encoding.ASCII.GetBytes(message);
                client.Send(data, data.Length, serverEP);
                Console.WriteLine($"Sent: {message}");
                
                // 4. 接收服务器响应
                IPEndPoint responseEP = new IPEndPoint(IPAddress.Any, 0);
                byte[] responseData = client.Receive(ref responseEP);
                string response = Encoding.ASCII.GetString(responseData);
                Console.WriteLine($"Received from {responseEP}: {response}");
            }
        }
        catch (SocketException e)
        {
            Console.WriteLine($"SocketException: {e}");
        }
        finally
        {
            client.Close();
        }
    }
}

3.3 UDP编程特点

1.​​无连接​​:不需要建立连接即可通信 2.​​不可靠​​:不保证数据包的顺序和到达 3.​​高效​​:比TCP更少的开销,适合实时应用 4.​​广播/多播​​:支持向多个目标同时发送数据 5.​​适合场景​​:视频流、在线游戏、DNS查询等

4.HTTP编程

4.1 HttpClient高级用法

using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;

class HttpClientAdvanced
{
    private static readonly HttpClient client = new HttpClient();
    
    public static async Task Run()
    {
        try
        {
            // 1. 配置HttpClient
            client.BaseAddress = new Uri("https://api.example.com/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
            client.Timeout = TimeSpan.FromSeconds(30);
            
            // 2. GET请求
            HttpResponseMessage response = await client.GetAsync("users/1");
            if (response.IsSuccessStatusCode)
            {
                string responseBody = await response.Content.ReadAsStringAsync();
                Console.WriteLine($"GET Response: {responseBody}");
            }
            
            // 3. POST请求
            var user = new { Name = "John Doe", Email = "john@example.com" };
            string json = Newtonsoft.Json.JsonConvert.SerializeObject(user);
            var content = new StringContent(json, Encoding.UTF8, "application/json");
            
            response = await client.PostAsync("users", content);
            if (response.IsSuccessStatusCode)
            {
                var createdUser = await response.Content.ReadAsAsync<dynamic>();
                Console.WriteLine($"Created user ID: {createdUser.id}");
            }
            
            // 4. 处理不同类型的响应
            response = await client.GetAsync("products");
            if (response.IsSuccessStatusCode)
            {
                // 方式1: 读取为字符串
                string responseString = await response.Content.ReadAsStringAsync();
                
                // 方式2: 读取为字节数组
                byte[] responseBytes = await response.Content.ReadAsByteArrayAsync();
                
                // 方式3: 读取为流
                using var stream = await response.Content.ReadAsStreamAsync();
                
                // 方式4: 直接反序列化为对象
                // var products = await response.Content.ReadAsAsync<List<Product>>();
            }
            
            // 5. 添加自定义请求头
            var request = new HttpRequestMessage(HttpMethod.Get, "secure-data");
            request.Headers.Add("X-Custom-Header", "value");
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "token123");
            
            response = await client.SendAsync(request);
        }
        catch (HttpRequestException e)
        {
            Console.WriteLine($"HTTP Request Exception: {e.Message}");
        }
        catch (TaskCanceledException e)
        {
            Console.WriteLine($"Request timeout: {e.Message}");
        }
    }
}

4.2 HttpClient最佳实践

1.单例模式​​:避免频繁创建HttpClient实例 2.​​超时设置​​:合理设置Timeout属性 3.​​取消支持​​:使用CancellationToken支持取消操作 4.​​连接池​​:利用ServicePointManager配置连接池 5.​​重试策略​​:实现指数退避重试机制 6.​​压缩支持​​:启用自动解压缩 7.​​证书验证​​:自定义服务器证书验证逻辑

5.高级

5.1 Socket编程

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class SocketAsyncServer
{
    private const int Port = 10000;
    private const int Backlog = 100;
    private const int BufferSize = 8192;
    
    private Socket _listener;
    private int _numConnectedSockets;
    private Semaphore _maxConnectionsSemaphore;
    
    public void Start()
    {
        _maxConnectionsSemaphore = new Semaphore(Backlog, Backlog);
        _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _listener.Bind(new IPEndPoint(IPAddress.Any, Port));
        _listener.Listen(Backlog);
        
        Console.WriteLine($"Server listening on port {Port}");
        
        StartAccept(null);
        
        Console.WriteLine("Press any key to stop the server...");
        Console.ReadKey();
    }
    
    private void StartAccept(SocketAsyncEventArgs acceptEventArg)
    {
        if (acceptEventArg == null)
        {
            acceptEventArg = new SocketAsyncEventArgs();
            acceptEventArg.Completed += AcceptCompleted;
        }
        else
        {
            acceptEventArg.AcceptSocket = null;
        }
        
        _maxConnectionsSemaphore.WaitOne();
        
        if (!_listener.AcceptAsync(acceptEventArg))
        {
            ProcessAccept(acceptEventArg);
        }
    }
    
    private void AcceptCompleted(object sender, SocketAsyncEventArgs e)
    {
        ProcessAccept(e);
    }
    
    private void ProcessAccept(SocketAsyncEventArgs e)
    {
        Interlocked.Increment(ref _numConnectedSockets);
        Console.WriteLine($"Client connection accepted. Total: {_numConnectedSockets}");
        
        SocketAsyncEventArgs readEventArgs = new SocketAsyncEventArgs();
        readEventArgs.Completed += IO_Completed;
        readEventArgs.SetBuffer(new byte[BufferSize], 0, BufferSize);
        readEventArgs.UserToken = new AsyncUserToken(e.AcceptSocket);
        
        if (!e.AcceptSocket.ReceiveAsync(readEventArgs))
        {
            ProcessReceive(readEventArgs);
        }
        
        StartAccept(e);
    }
    
    private void IO_Completed(object sender, SocketAsyncEventArgs e)
    {
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Receive:
                ProcessReceive(e);
                break;
            case SocketAsyncOperation.Send:
                ProcessSend(e);
                break;
            default:
                throw new ArgumentException("Invalid operation completed");
        }
    }
    
    private void ProcessReceive(SocketAsyncEventArgs e)
    {
        AsyncUserToken token = (AsyncUserToken)e.UserToken;
        
        if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
        {
            string received = Encoding.ASCII.GetString(e.Buffer, e.Offset, e.BytesTransferred);
            Console.WriteLine($"Received: {received} from {token.Socket.RemoteEndPoint}");
            
            // 回显数据
            byte[] response = Encoding.ASCII.GetBytes($"Echo: {received}");
            e.SetBuffer(response, 0, response.Length);
            
            if (!token.Socket.SendAsync(e))
            {
                ProcessSend(e);
            }
        }
        else
        {
            CloseClientSocket(e);
        }
    }
    
    private void ProcessSend(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            AsyncUserToken token = (AsyncUserToken)e.UserToken;
            
            // 准备接收下一个数据包
            e.SetBuffer(new byte[BufferSize], 0, BufferSize);
            
            if (!token.Socket.ReceiveAsync(e))
            {
                ProcessReceive(e);
            }
        }
        else
        {
            CloseClientSocket(e);
        }
    }
    
    private void CloseClientSocket(SocketAsyncEventArgs e)
    {
        AsyncUserToken token = e.UserToken as AsyncUserToken;
        
        try
        {
            token.Socket.Shutdown(SocketShutdown.Send);
        }
        catch (Exception) { }
        
        token.Socket.Close();
        
        Interlocked.Decrement(ref _numConnectedSockets);
        _maxConnectionsSemaphore.Release();
        Console.WriteLine($"Client disconnected. Total: {_numConnectedSockets}");
    }
}

class AsyncUserToken
{
    public Socket Socket { get; set; }
    
    public AsyncUserToken(Socket socket)
    {
        Socket = socket;
    }
}

5.2 网络性能优化

​缓冲区管理​​:

  • 使用BufferManager类重用缓冲区
  • 避免频繁分配和释放内存 连接池配置​​:
ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.MaxServicePoints = 1000;
ServicePointManager.UseNagleAlgorithm = false;

​Socket选项​​:

socket.NoDelay = true; // 禁用Nagle算法
socket.ReceiveBufferSize = 8192;
socket.SendBufferSize = 8192;
socket.ReceiveTimeout = 5000;
socket.SendTimeout = 5000;

6. 安全考虑

6.1 SSL/TLS加密通信

// 创建安全的TcpListener
TcpListener listener = new TcpListener(IPAddress.Any, 443);
using var sslStream = new SslStream(client.GetStream(), false);
X509Certificate2 cert = new X509Certificate2("server.pfx", "password");
sslStream.AuthenticateAsServer(cert);

// 客户端验证
var handler = new HttpClientHandler
{
    ClientCertificateOptions = ClientCertificateOption.Manual,
    ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) =>
    {
        // 自定义证书验证逻辑
        return policyErrors == System.Net.Security.SslPolicyErrors.None;
    }
};

6.2 认证与授权

// 基本认证
var byteArray = Encoding.ASCII.GetBytes("username:password");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));

// OAuth 2.0 Bearer Token
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "access_token");

// API Key
client.DefaultRequestHeaders.Add("X-API-KEY", "your-api-key");