ZetCode

C# 套接字

最后修改于 2023 年 7 月 5 日

在本文中,我们将展示如何在 C# 中使用 Socket。

网络协议

TCP/IP 是一组协议,设备使用它通过互联网和大多数本地网络进行通信。TCP 更加可靠,具有广泛的错误检查,并且需要更多资源。它被 HTTP、SMTP 或 FTP 等服务使用。UDP 的可靠性要差得多,错误检查有限,并且需要的资源更少。它被 VoIP 等服务使用。

Socket(套接字)

在编程中,套接字(Socket) 是在网络上运行的两个程序之间通信的端点。在 .NET 中,Socket 类为网络通信提供了一组丰富的方法和属性。它允许我们执行同步和异步数据传输。

C# UDP Socket 示例

UDP 是一种通信协议,它在网络上传输独立的包,不保证到达,也不保证传递顺序。Quote of the Day (QOTD) 是使用 UDP 的一项服务。

Program.cs
using System.Text;
using System.Net;
using System.Net.Sockets;

string server = "djxmmx.net";
int port = 17;

byte[] data = new byte[0];
IPHostEntry hostEntry = Dns.GetHostEntry(server);

var ipe = new IPEndPoint(hostEntry.AddressList[0], port);
using var socket = new Socket(AddressFamily.InterNetworkV6,
    SocketType.Dgram, ProtocolType.Udp);

socket.ReceiveTimeout = 5000;
socket.SendTimeout = 2000;

socket.SendTo(data, ipe);

byte[] data2 = new byte[1024];
EndPoint remote = (EndPoint)ipe;

int n = socket.ReceiveFrom(data2, ref remote);

Console.WriteLine($"Message size: {n}");
Console.WriteLine($"Messaged received from: {remote}");
Console.WriteLine(Encoding.ASCII.GetString(data2, 0, n));

该程序创建一个客户端程序,连接到 QOTD 服务。

string server = "djxmmx.net";
int port = 17;

这是可以找到 QOTD 服务的服务器和端口。请注意,此类服务通常是短期的。

byte[] data = new byte[0];

我们向服务器发送一条空消息。

IPHostEntry hostEntry = Dns.GetHostEntry(server);

Dns.GetHostEntry 将主机名或 IP 地址解析为 IPHostEntry 实例。 这是用于 Internet 主机地址信息的 .NET 容器类。

using var socket = new Socket(AddressFamily.InterNetworkV6,
    SocketType.Dgram, ProtocolType.Udp);

创建一个 Socket。 我们传递寻址方案、套接字类型和协议类型。

socket.ReceiveTimeout = 5000;
socket.SendTimeout = 2000;

我们设置接收和发送数据的超时时间。

socket.SendTo(data, ipe);

我们使用 SendTo 将数据(在本例中为空)发送到终结点。

byte[] data2 = new byte[1024];

在这里,我们将存储响应消息。

int n = socket.ReceiveFrom(data2, ref remote);

ReceiveFrom 方法将数据报接收到数据缓冲区中并存储终结点。 它返回接收到的字节数。

Console.WriteLine($"Message size: {n}");
Console.WriteLine($"Messaged received from: {remote}");
Console.WriteLine(Encoding.ASCII.GetString(data2, 0, n));

我们打印消息大小、删除地址以及接收到的数据。

$ dotnet run
Message size: 73
Messaged received from: [2001:470:e312:10::10]:17
"Take what you can, give nothing back..."
         - Pirates Of The Caribbean

C# Socket HTTP HEAD 请求

HEAD 请求是一种 HTTP GET 请求,但不包含消息正文。请求/响应的头部包含元数据,例如 HTTP 协议版本或内容类型。

Program.cs
using System.Text;
using System.Net;
using System.Net.Sockets;

string server = "webcode.me";
int port = 80;

var request = "HEAD / HTTP/1.0\r\n\r\n";

byte[] bReq = Encoding.ASCII.GetBytes(request);
byte[] bRec = new byte[8192];

IPHostEntry hostEntry = Dns.GetHostEntry(server);

var ipe = new IPEndPoint(hostEntry.AddressList[0], port);
using var socket = new Socket(AddressFamily.InterNetwork,
    SocketType.Stream, ProtocolType.Tcp);

socket.Connect(ipe);

if (socket.Connected)
{
    Console.WriteLine("Connection established");
}
else
{
    Console.WriteLine("Connection failed");
    return;
}

socket.Send(bReq, bReq.Length, 0);

int n = socket.Receive(bRec, bRec.Length, 0);
Console.WriteLine(Encoding.ASCII.GetString(bRec, 0, n));

在代码示例中,我们向 webcode.me 发送 HEAD 请求。

string server = "webcode.me";
int port = 80;

这是服务器名称和端口号。

string request = "HEAD / HTTP/1.0\r\n\r\n";

head 请求通过 HEAD 命令发出,后跟资源 URL 和 HTTP 协议版本。 请注意,\r\n 字符是通信过程的强制组成部分。 详细信息在 RFC 7231 文档中进行了描述。

using var socket = new Socket(AddressFamily.InterNetwork,
    SocketType.Stream, ProtocolType.Tcp);

HTTP 协议(HTTP/0.9 到 HTTP/2)是基于 TCP 的。

if (socket.Connected)

我们可以通过 Connected 属性检查是否已成功连接。

socket.Send(bReq, bReq.Length, 0);

我们使用 Send 发送数据。

int n = socket.Receive(bRec, bRec.Length, 0);
Console.WriteLine(Encoding.ASCII.GetString(bRec, 0, n));

我们接收并打印标头数据。

$ dotnet run
Connection established
HTTP/1.1 200 OK
Server: nginx/1.6.2
Date: Wed, 21 Sep 2022 17:43:39 GMT
Content-Type: text/html
Content-Length: 394
Last-Modified: Sun, 23 Jan 2022 10:39:25 GMT
Connection: close
ETag: "61ed305d-18a"
Accept-Ranges: bytes

C# Socket HTTP GET 请求

HTTP GET 方法请求指定资源的表示。使用 GET 的请求应该只检索数据。

Program.cs
using System.Text;
using System.Net;
using System.Net.Sockets;

string server = "webcode.me";
int port = 80;

string request = $"GET / HTTP/1.1\r\nHost: {server} \r\nConnection: Close\r\n\r\n";

Byte[] bReq = Encoding.ASCII.GetBytes(request);
Byte[] bRec = new Byte[1024];

IPHostEntry hostEntry = Dns.GetHostEntry(server);

var ipe = new IPEndPoint(hostEntry.AddressList[0], port);
using var socket = new Socket(AddressFamily.InterNetwork,
    SocketType.Stream, ProtocolType.Tcp);

socket.Connect(ipe);

if (socket.Connected)
{
    Console.WriteLine("Connection established");
}
else
{
    Console.WriteLine("Connection failed");
    return;
}

socket.Send(bReq, bReq.Length, 0);

int n = 0;
var sb = new StringBuilder();

do
{
    n = socket.Receive(bRec, bRec.Length, 0);
    sb.Append(Encoding.ASCII.GetString(bRec, 0, n));
}
while (n > 0);

Console.WriteLine(sb);

该示例使用 GET 请求读取 webcode.me 的主页。

string request = $"GET / HTTP/1.1\r\nHost: {server} \r\nConnection: Close\r\n\r\n";

我们向 Socket 写入了一个简单的 GET 请求。

do
{
    n = socket.Receive(bRec, bRec.Length, 0);
    sb.Append(Encoding.ASCII.GetString(bRec, 0, n));
}
while (n > 0);

由于我们无法确定响应的大小,因此我们使用 do/while 循环以块的形式读取响应。

来源

Socket 类 - 语言参考

在本文中,我们介绍了如何在 C# 中使用 Socket。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直在撰写编程文章。到目前为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 C# 教程