三.网络编程
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");