Skip to content

Commit ff79087

Browse files
authored
Improve the class-level Socket example (#8602)
- Remove the complicated ConnectSocket method, and leave DNS-resolution for the socket itself - Demonstrate low-allocation buffering - Introduce an async version of the same example - Emphasize the benefits of using cancellable overloads, including the implicit performance benefit: cancellable overloads always return a ValueTask - Delete VB and C++/CLI examples; the maintenance burden is not worth the benefits.
1 parent 895c378 commit ff79087

File tree

4 files changed

+110
-269
lines changed
  • snippets
    • cpp/VS_Snippets_Remoting/System.Net.Sockets.Socket/CPP
    • csharp/System.Net.Sockets/Socket/Overview
    • visualbasic/VS_Snippets_Remoting/System.Net.Sockets.Socket/VB
  • xml/System.Net.Sockets

4 files changed

+110
-269
lines changed

snippets/cpp/VS_Snippets_Remoting/System.Net.Sockets.Socket/CPP/socket.cpp

-96
This file was deleted.

snippets/csharp/System.Net.Sockets/Socket/Overview/socket.cs

+97-77
Original file line numberDiff line numberDiff line change
@@ -3,97 +3,117 @@
33
* This example creates a socket connection to the server specified by the user,
44
* using port 80. Once the connection has been established it asks the server for
55
* the content of its home page. If no server name is passed as argument to this
6-
* program, it sends the request to the current machine.
6+
* program, it sends the request to example.com.
77
* */
8-
//<Snippet1>
98
using System;
109
using System.Text;
11-
using System.IO;
12-
using System.Net;
1310
using System.Net.Sockets;
11+
using System.Threading.Tasks;
12+
using System.Threading;
13+
using System.Linq;
1414

1515
public class GetSocket
1616
{
17-
private static Socket ConnectSocket(string server, int port)
17+
public static async Task Main(string[] args)
1818
{
19-
Socket s = null;
20-
IPHostEntry hostEntry = null;
21-
22-
// Get host related information.
23-
hostEntry = Dns.GetHostEntry(server);
24-
25-
// Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
26-
// an exception that occurs when the host IP Address is not compatible with the address family
27-
// (typical in the IPv6 case).
28-
foreach(IPAddress address in hostEntry.AddressList)
29-
{
30-
IPEndPoint ipe = new IPEndPoint(address, port);
31-
Socket tempSocket =
32-
new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
33-
34-
tempSocket.Connect(ipe);
35-
36-
if(tempSocket.Connected)
37-
{
38-
s = tempSocket;
39-
break;
40-
}
41-
else
42-
{
43-
continue;
44-
}
45-
}
46-
return s;
19+
Uri? uri = args.Any() ? new Uri(args[0]) : null;
20+
21+
// Sync:
22+
SendHttpRequest(uri);
23+
24+
// Async:
25+
await SendHttpRequestAsync(uri);
4726
}
4827

49-
// This method requests the home page content for the specified server.
50-
private static string SocketSendReceive(string server, int port)
28+
//<Snippet1>
29+
private static void SendHttpRequest(Uri? uri = null, int port = 80)
30+
{
31+
uri ??= new Uri("http://example.com");
32+
33+
// Construct a minimalistic HTTP/1.1 request
34+
byte[] requestBytes = Encoding.ASCII.GetBytes(@$"GET {uri.AbsoluteUri} HTTP/1.0
35+
Host: {uri.Host}
36+
Connection: Close
37+
38+
");
39+
40+
// Create and connect a dual-stack socket
41+
using Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
42+
socket.Connect(uri.Host, port);
43+
44+
// Send the request.
45+
// For the tiny amount of data in this example, the first call to Send() will likely deliver the buffer completely,
46+
// however this is not guaranteed to happen for larger real-life buffers.
47+
// The best practice is to iterate until all the data is sent.
48+
int bytesSent = 0;
49+
while (bytesSent < requestBytes.Length)
5150
{
52-
string request = "GET / HTTP/1.1\r\nHost: " + server +
53-
"\r\nConnection: Close\r\n\r\n";
54-
Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
55-
Byte[] bytesReceived = new Byte[256];
56-
string page = "";
57-
58-
// Create a socket connection with the specified server and port.
59-
using(Socket s = ConnectSocket(server, port)) {
60-
61-
if (s == null)
62-
return ("Connection failed");
63-
64-
// Send request to the server.
65-
s.Send(bytesSent, bytesSent.Length, 0);
66-
67-
// Receive the server home page content.
68-
int bytes = 0;
69-
page = "Default HTML page on " + server + ":\r\n";
70-
71-
// The following will block until the page is transmitted.
72-
do {
73-
bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
74-
page = page + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
75-
}
76-
while (bytes > 0);
77-
}
78-
79-
return page;
51+
bytesSent += socket.Send(requestBytes, bytesSent, requestBytes.Length - bytesSent, SocketFlags.None);
8052
}
8153

82-
public static void Main(string[] args)
54+
// Do minimalistic buffering assuming ASCII response
55+
byte[] responseBytes = new byte[256];
56+
char[] responseChars = new char[256];
57+
58+
while (true)
8359
{
84-
string host;
85-
int port = 80;
86-
87-
if (args.Length == 0)
88-
// If no server name is passed as argument to this program,
89-
// use the current host name as the default.
90-
host = Dns.GetHostName();
91-
else
92-
host = args[0];
93-
94-
string result = SocketSendReceive(host, port);
95-
Console.WriteLine(result);
60+
int bytesReceived = socket.Receive(responseBytes);
61+
62+
// Receiving 0 bytes means EOF has been reached
63+
if (bytesReceived == 0) break;
64+
65+
// Convert byteCount bytes to ASCII characters using the 'responseChars' buffer as destination
66+
int charCount = Encoding.ASCII.GetChars(responseBytes, 0, bytesReceived, responseChars, 0);
67+
68+
// Print the contents of the 'responseChars' buffer to Console.Out
69+
Console.Out.Write(responseChars, 0, charCount);
9670
}
9771
}
98-
9972
//</Snippet1>
73+
74+
//<Snippet2>
75+
private static async Task SendHttpRequestAsync(Uri? uri = null, int port = 80, CancellationToken cancellationToken = default)
76+
{
77+
uri ??= new Uri("http://example.com");
78+
79+
// Construct a minimalistic HTTP/1.1 request
80+
byte[] requestBytes = Encoding.ASCII.GetBytes(@$"GET {uri.AbsoluteUri} HTTP/1.1
81+
Host: {uri.Host}
82+
Connection: Close
83+
84+
");
85+
86+
// Create and connect a dual-stack socket
87+
using Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
88+
await socket.ConnectAsync(uri.Host, port, cancellationToken);
89+
90+
// Send the request.
91+
// For the tiny amount of data in this example, the first call to SendAsync() will likely deliver the buffer completely,
92+
// however this is not guaranteed to happen for larger real-life buffers.
93+
// The best practice is to iterate until all the data is sent.
94+
int bytesSent = 0;
95+
while (bytesSent < requestBytes.Length)
96+
{
97+
bytesSent += await socket.SendAsync(requestBytes.AsMemory(bytesSent), SocketFlags.None);
98+
}
99+
100+
// Do minimalistic buffering assuming ASCII response
101+
byte[] responseBytes = new byte[256];
102+
char[] responseChars = new char[256];
103+
104+
while (true)
105+
{
106+
int bytesReceived = await socket.ReceiveAsync(responseBytes, SocketFlags.None, cancellationToken);
107+
108+
// Receiving 0 bytes means EOF has been reached
109+
if (bytesReceived == 0) break;
110+
111+
// Convert byteCount bytes to ASCII characters using the 'responseChars' buffer as destination
112+
int charCount = Encoding.ASCII.GetChars(responseBytes, 0, bytesReceived, responseChars, 0);
113+
114+
// Print the contents of the 'responseChars' buffer to Console.Out
115+
await Console.Out.WriteAsync(responseChars.AsMemory(0, charCount), cancellationToken);
116+
}
117+
}
118+
//</Snippet2>
119+
}

snippets/visualbasic/VS_Snippets_Remoting/System.Net.Sockets.Socket/VB/socket.vb

-93
This file was deleted.

0 commit comments

Comments
 (0)