.NET类库精髓 之 System.Net.Socket 命名空间
引言
最近正在研究.NET类库,研究System.Threading之后准备研究System.Net.Socket命名空间,计划学习完System.NetSocket命名空间之后可以写一个Server/Client程序,Client可以接受Server发送来的指令,Client可以运行相应的操作,Client可以返回结果给Server。
在System.Net.Socket 命名空间下包含一些重要的类分别是:
TcpListener 类
TcpClient类
NetworkStream 类
Socket 类
UdpClient 类
我们分为两个章节来介绍,第一章来介绍TCP通信 第二章来介绍UDP通信。
写作的目的:一方面学习之后进行思考和总结,使学得更加扎实一些,另一方面分享给大家,大家可以做个参考。
第一章TCP 通信
TCP通信简介(这部分需要参考TCP/IP通信):
TCP建立连接过程:
- Server监听某个端口,等待Client接入。
- Client向Server发送连接请求。
- Server接受Client的连接请求,然后创建一个TcpClient来用于与Client进行通信。Client与Server可以进行通信,发送和读取数据。
- Server继续监听其他Client的连接。
TCP断开连接过程:
TcpListener 类
TcpListener 类介绍:
监听TCP网络中的客户端的连接。
TcpListener 类注意:
TcpListener 类提供了简单的方法用来监听和接受阻塞同步模式的客户端连接请求。可以使用TcpClient或者Socket来连接服务器的特定端口。
使用Start方法来开始监听客户端的请求,使用AcceptSocket或者AcceptTcpClient来与客户端建立一个连接,这两个方法将会阻塞,如果想避免阻塞可以先使用Pending方法来检测连接请求是否可用。
调用Stop方法来关闭TcpListener.
TcpListener 类举例:
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
class MyTcpListener
{
public static void
{
TcpListener server = null;
try
{
//设置TcpListener监听端口为2617.
Int32 port = 2617;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
//创建 Server绑定到本机的2617端口;
server = new TcpListener(localAddr, port);
// 开始监听客户端的请求
server.Start();
// 创建用于读取数据的Buffer.
Byte[] bytes = new Byte[256];
String data = null;
// 进入监听循环,当运行到AcceptTcpClient时会检查在请求队列中是否有客户端请求,如果没有请求就会Block,下次得到调度后再次检查是否有客户端请求.
while (true)
{
Console.Write("Waiting for a connection... ");
// 接受连接,创建新的TcpClient用于和Client绑定进行通信
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
data = null;
// 获取NetworkStream对象用于读写数据
NetworkStream stream = client.GetStream();
int i;
// 循环接收客户端发送来的所有数据
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// 将数据转化为ASCII字符串
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
// 处理数据然后发送到客户端.
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
// 为客户端发送回应信息.
stream.Write(msg, 0, msg.Length);
Console.WriteLine("Sent: {0}", data);
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Console.WriteLine("\nHit enter to continue...");
Console.Read();
}
}
TcpListener Members
TcpListener.AcceptTcpClient
简介:
接受客户端请求。
注意:
AcceptTcpClient 是一个阻塞方法将返回一个可以用来发送和接受数据的TcpClient,使用Pending 方法来检查是否有可用的客户端连接请求。
使用TcpClient.GetStream 方法来获得 TcpClient返回的NetworkStream。NetworkStream 将会提供与远程客户端读写的方法。
举例:
下面的代码演示,AcceptTcpClient 方法用来返回 TcpClient.TcpClient 用来和连接的客户端进行通信。
// Create a socket and connect with a remote host.
IPHostEntry myIpHostEntry = Dns.Resolve("www.contoso.com");
IPEndPoint myIpEndPoint = new IPEndPoint(myIpHostEntry.AddressList[0], 1001);
Socket mySocket = new Socket(myIpEndPoint.Address.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
try{
mySocket.Connect(myIpEndPoint);
// Create the NetworkStream for communicating with the remote host.
NetworkStream myNetworkStream;
if (networkStreamOwnsSocket){
myNetworkStream = new NetworkStream(mySocket, true);
}
else{
myNetworkStream = new NetworkStream(mySocket);
}
// Check to see if this NetworkStream is writable.
if (myNetworkStream.CanWrite){
byte[] myWriteBuffer = Encoding.ASCII.GetBytes("Are you receiving this message?");
myNetworkStream.Write(myWriteBuffer, 0, myWriteBuffer.Length);
}
else{
Console.WriteLine("Sorry. You cannot write to this NetworkStream.");
}
// Check to see if this NetworkStream is readable.
if(myNetworkStream.CanRead){
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do{
numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
}
while(myNetworkStream.DataAvailable);
// Print out the received message to the console.
Console.WriteLine("You received the following message : " +
myCompleteMessage);
}
else{
Console.WriteLine("Sorry. You cannot read from this NetworkStream.");
}
// Close the NetworkStream
myNetworkStream.Close();
}
catch (Exception exception){
Console.WriteLine("Exception Thrown: " + exception.ToString());
}
}
还有一些总要的方法就不一一演示了,具体使用可以参考MSDN:
AcceptSocket
Start
Stop
Pending
TcpClient类
简介:
注意:
举例:
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace TcpClientExample
{
class Program
{
static void
{
Connect("127.0.0.1", "Hello World.");
}
static void Connect(String server, String message)
{
try
{
// 创建TcpClient时连接到服务器
Int32 port = 2617;
TcpClient client = new TcpClient(server, port);
// 将传送的消息转化为ASCII 然后存储在字节数组中
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// 获得TcpClient流来进行读写操作
NetworkStream stream = client.GetStream();
// 发送数据到连接的TcpServer上
stream.Write(data, 0, data.Length);
Console.WriteLine("Sent: {0}", message);
// 获得服务器的回应
// 用来存储回应信息的Buffer.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);
// 关闭网络流 关闭客户端
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Console.WriteLine("ArgumentNullException: {0}", e);
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
Console.WriteLine("\n Press Enter to continue...");
Console.Read();
}
}
}
NetworkStream 类
Socket 类
介绍:
实现伯克利Socket接口。
Socket 类为网络通信提供丰富的方法和属性。Socket 类允许使用任何协议进行同步和异步数据传输,例如同步方法 Receive 异步方法 BeginReceive 和 EndReceive方法。
如果你的应用程序只有一个线程,使用下列方法来进行同步操作。
如果你使用面向连接的协议例如TCP,你的服务器可以使用 Listen来监听连接,Accept 方法来处理客户端的连接,然后返回 你可以与远程主机通信的Socket。使用返回的Socket 调用Send 或者 Receive方法。在监听之前调用Bind 方法来绑定地址和端口。如果希望连接到监听的主机,可以调用Connect 方法,然后使用Send 或者 Receive方法进行数据读写。
如果你使用非面向连接的协议,比如 UDP协议,不需要进行监听,调用ReceiveFrom 方法来接收入站数据,使用SendTo 方法将数据发送到远程主机。
执行期间使用多线程来处理,使用下列方法,下列方法为异步操作模式。
如果使用面向连接连接协议,如 TCP 使用Socket的 BeginConnet 和 EndConnect 方法来连接正在监听的主机。 使用BeginSend 和 EndSend 或者BeginReceive 和 EndReceive 方法来异步通信。 入站的连接请求使用 BeginAccept 和 EndAccept来进行处理。
如果使用非面向对象的连接协议,如UDP,你可以使用 BeginSendTo 和EndSendTo 来发送数据包,使用BeginReceiveFrom 和 EndReceiveFrom来接收数据包。
当完成发送和接受任务之后,使用Shutdown 方法来关闭Socket。在调用Shutdown 之后调用 Close方法来释放Socket的资源。
Socket 类允许你使用 SetSocketOption配置Socket,利用GetSocketOption来获得设置信息。
注意:
举例:
下面代码演示如何使用Socket类发送数据到HTTP 服务器和接受服务器的回应。本例子阻塞知道整个页面收到。
using System;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
public class GetSocket
{
private static Socket ConnectSocket(string server, int port)
{
Socket s = null;
IPHostEntry hostEntry = null;
// Get host related information.
hostEntry = Dns.GetHostEntry(server);
// Loop through the AddressList to obtain the supported AddressFamily. This is to avoid
// an exception that occurs when the host IP Address is not compatible with the address family
// (typical in the IPv6 case).
foreach (IPAddress address in hostEntry.AddressList)
{
IPEndPoint ipe = new IPEndPoint(address, port);
Socket tempSocket =
new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
tempSocket.Connect(ipe);
if (tempSocket.Connected)
{
s = tempSocket;
break;
}
else
{
continue;
}
}
return s;
}
// This method requests the home page content for the specified server.
private static string SocketSendReceive(string server, int port)
{
string request = "GET / HTTP/1.1\r\nHost: " + server +
"\r\nConnection: Close\r\n\r\n";
Byte[] bytesSent = Encoding.ASCII.GetBytes(request);
Byte[] bytesReceived = new Byte[256];
// Create a socket connection with the specified server and port.
Socket s = ConnectSocket(server, port);
if (s == null)
return ("Connection failed");
// Send request to the server.
s.Send(bytesSent, bytesSent.Length, 0);
// Receive the server home page content.
int bytes = 0;
string page = "Default HTML page on " + server + ":\r\n";
// The following will block until te page is transmitted.
do
{
bytes = s.Receive(bytesReceived, bytesReceived.Length, 0);
page = page + Encoding.ASCII.GetString(bytesReceived, 0, bytes);
}
while (bytes > 0);
return page;
}
public static void
{
string host;
int port = 80;
if (args.Length == 0)
// If no server name is passed as argument to this program,
// use the current host name as the default.
host = Dns.GetHostName();
else
host = args[0];
string result = SocketSendReceive(host, port);
Console.WriteLine(result);
}
}