引自:http://ilewen.com/questions/514编程
在本教程中,我会向你展现如何用C#创建一个线程中的TCP服务端。若是你用过windows的sockets编写程序,你就知道有多麻烦。感谢.net框架,使得网络编程变得更容易了。windows
咱们将创建一个很是简单的的服务器接受客户端链接,并能够发送和接收数据。服务器为每个链接客户端产生一个线程,从理论上讲,能够接受多个链接(虽在实践中,Windows对此是有限制)。
下面看代码:数组
using System; using System.Text; using System.Net.Sockets; using System.Threading; using System.Net; namespace TCPServerTutorial { class Server { private TcpListener tcpListener; private Thread listenThread; public Server() { this.tcpListener = new TcpListener(IPAddress.Any, 3000); this.listenThread = new Thread(new ThreadStart(ListenForClients)); this.listenThread.Start(); } } }
上面创建了一个基本的服务器类。咱们定义了一个TcpListener变量(TcpListener封装了底层套接字通讯工做),同时定义了一个线程用于监听客户端的链接。接下来咱们定义了ThreadStart的委托函数:ListenForClients。
代码以下:服务器
private void ListenForClients() { this.tcpListener.Start(); while (true) { //blocks until a client has connected to the server TcpClient client = this.tcpListener.AcceptTcpClient(); //create a thread to handle communication //with connected client Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm)); clientThread.Start(client); } }
这个函数很是简单。首先,它启动TcpListener,而后循环接受链接。 AcceptTcpClient的调用将阻塞线程的执行,直到一个客户端链接,在这里,咱们触发一个线程来处理与咱们的新客户端的通讯。我用了ParameterizedThreadStart委托,因此我能够传递AcceptTcpClient调用返回的TcpClient对象到新线程。网络
ParameterizedThreadStart使用函数HandleClientComm。这个函数负责从客户端读取数据。让咱们看看它。并发
private void HandleClientComm(object client) { TcpClient tcpClient = (TcpClient)client; NetworkStream clientStream = tcpClient.GetStream(); byte[] message = new byte[4096]; int bytesRead; while (true) { bytesRead = 0; try { //blocks until a client sends a message bytesRead = clientStream.Read(message, 0, 4096); } catch { //a socket error has occured break; } if (bytesRead == 0) { //the client has disconnected from the server break; } //message has successfully been received ASCIIEncoding encoder = new ASCIIEncoding(); System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead)); } tcpClient.Close(); }
第一步将client类型转换为TcpClient类型,由于ParameterizedThreadStart委托只能接受基本对象类型。下一步,从TcpClient获得NetworkStream用来读取数据。以后,经过一个while循环从客户端读取数据。 read调用会一直处于阻塞状态,直到从客户端接收到数据。若是从客户端读取到零字节,那么说明客户端已断开。在该例子里,我只是一个字符串转换成字节数组,并将它输出到控制台。固然,你会作一些更复杂的工做。若是socket出现错误或客户端断开链接,你应该调用TcpClient对象的close函数关闭链接,释放它使用的任何资源。框架
上面就是建立一个服务器线程,接受链接,并从客户端读取数据所需作的全部的事情。固然,若是服务端不能发送数据,那么就没什么用了。下面,让咱们看看如何将数据发送到咱们的链接的一个客户端。socket
NetworkStream clientStream = tcpClient.GetStream(); ASCIIEncoding encoder = new ASCIIEncoding(); byte[] buffer = encoder.GetBytes("Hello Client!"); clientStream.Write(buffer, 0 , buffer.Length); clientStream.Flush();
从AcceptTcpClient返回的TcpClient对象用来发送数据给客户端。所以须要在服务端保存这些对象。一般能够建立一个TcpClient对象的集合。发送数据是很是简单的,只须要获得client的NetworkStream对象,而后调用它的write方法就能够了。tcp
TCP服务端已经完成了。比较难的部分是定义一个协议用来在客户端和服务端之间发送信息。应用层的协议一般对不一样的应用都是不同的。因此我不打算讲解更多,你只须要实现你本身的。函数
若是没有一个客户端链接到服务端,那么这个服务端还有存在的意思吗?本教程主要是讲服务端编程,但这里有一个简短的代码,说明了如何设置一个基本的TCP链接,并发送一段数据。
TcpClient client = new TcpClient(); IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3000); client.Connect(serverEndPoint); NetworkStream clientStream = client.GetStream(); ASCIIEncoding encoder = new ASCIIEncoding(); byte[] buffer = encoder.GetBytes("Hello Server!"); clientStream.Write(buffer, 0 , buffer.Length); clientStream.Flush();
第一步工做是获取客户端到服务端的链接。使用TcpClient.Connect方法。它须要知道服务端的IPEndPoint,在这里,我将链接到本地主机,端口号3000。而后发送字符串"hello Server!"
注意:从客户端或服务器写并不老是等于一个在接收端读。例如,客户端向服务器发送10个字节,但服务器可能没法在第一次读取的时候获得全部10个字节。使用TCP,几乎保证最终获得全部10个字节,但它可能须要不止读取一次。因此当设计交互协议的时候要注意这一点。