网络编程序列2——C#TCP服务端代码实现二



1.1.同步操作模式

如果当前使用的是面向连接的协议(如 TCP),则服务器可以使用Listen方法侦听连接。Accept方法处理任何传入的连接请求,并返回可用于与远程主机进行数据通信的Socket。可以使用此返回的Socket来调用SendReceive方法。如果要指定本地 IP 地址和端口号,请在调用Listen方法之前先调用Bind方法。如果您希望基础服务提供程序为您分配可用端口,请使用端口号 0。如果希望连接到侦听主机,请调用Connect方法。若要进行数据通信,请调用SendReceive方法。

如果当前使用的是无连接协议(如 UDP),则根本不需要侦听连接。调用ReceiveFrom方法可接受任何传入的数据报。使用SendTo方法可将数据报发送到远程主机。

1.1.1Accept方法处理任何传入的连接请求(服务端)

Socket socket =new Socket(AddressFamily.InterNetwork, SocketType.Stream,  ProtocolType.Tcp);// 创建socket对象

IPAddress hostIP = (Dns.Resolve(IPAddress.Any.ToString())).AddressList[0];// 获取主机IP

IPEndPoint ep =new IPEndPoint(hostIP, port);

listenSocket.Bind(ep);

// start listening

listenSocket.Listen(0);

Socket serverSocket =socket.Accept();//等待客户端连接,阻塞等待;

byte[] recByte =newbyte[4096];

int bytes = serverSocket.Receive(recByte, recByte.Length,0);//读取数据

string sendStr ="send to client :hello";

byte[] sendByte =Encoding.ASCII.GetBytes(sendStr);

serverSocket.Send(sendByte, sendByte.Length,0);//发送数据

serverSocket.Close();

socket.Close();

(TCP客户端)相对简单

如果希望连接到侦听主机,请调用Connect方法。若要进行数据通信,请调用SendReceive方法。

Socket clientSocket=new Socket(AddressFamily.InterNetwork, SocketType.Stream,  ProtocolType.Tcp);// 创建socket对象

IPEndPoint ep =new IPEndPoint(serverIP, port);

clientSocket.Connect(ep);


//发信息

sendStr ="send to server : hello,ni hao";

byte[] sendBytes =Encoding.ASCII.GetBytes(sendStr);

clientSocket.Send(sendBytes);//调用Send

//接收信息

byte[] recBytes =newbyte[4096];

int bytes = clientSocket.Receive(recBytes, recBytes.Length,0);//调用Receive

clientSocket.Close();//关闭连接


1.2. 异步操作模式

如果当前使用的是面向连接的协议(如 TCP),则可使用SocketBeginConnectEndConnect方法来连接侦听主机。通过使用BeginSendEndSend方法,或者使用BeginReceiveEndReceive方法,可以进行异步数据通信。可以使用BeginAcceptEndAccept处理传入的连接请求。

如果您使用的是 UDP 等无连接协议,则可以使用BeginSendToEndSendTo来发送数据报,而使用BeginReceiveFromEndReceiveFrom来接收数据报。

1.2.1 BeginAcceptEndAccept处理传入的连接请求



//启动监听的线程

ListenThread = new Thread(ServerListenThread);

ListenThread.Name = "ListenThread";

ListenThread.IsBackground = true;  //后台运行

ListenThread.Start();

//后台的监听线程

//通过将布尔值传递给构造函数来控制ManualResetEvent的初始状态,如果初始状态处于终止状态,为true;否则为false。

private ManualResetEvent ServerAcceptDone = new ManualResetEvent(false);      //非终止状态,遇到WaitOne阻塞//也可以考虑使用EventWaitHandle

//终止状态时WaitOne()允许线程访问下边的语句

//非终止状态时WaitOne()阻塞线程,不允许线程访问下边的语句

private void ServerListenThread()

{

while (true)

{

//把非终止状态改为终止状态用Set()方法

//把终止状态改为非终止状态用Reset()方法,此时线程进入ManualEnent,调用WaitOne()将此线程阻止;

ServerAcceptDone.Reset();//重启异步连接

ServerSocket.BeginAccept(new AsyncCallback(AcceptCallback), ServerSocket);

ServerAcceptDone.WaitOne();      //等待接受客户端

}

}

public static void AcceptCallback(IAsyncResult ar)

{

//接受回调方法。该方法的此节向主应用程序线程发出信号,让它继续处理并建立与客户端的连接

ServerAcceptDone.Set();//将信号设为终止,监听线程继续执行等待新的连接;

//获取客户端请求句柄

Socket listener = (Socket)ar.AsyncState;//

Socket handler = listener.EndAccept(ar);//完成对BeginAccept的调用 获取当前客户端

ServerStateObject state =new ServerStateObject();

state.workSocket = handler;

handler.BeginReceive(state.buffer, 0, state.BUFFER_SIZE, 0,new AsyncCallback(ReadCallback),state);//开始从连接的Socket中异步接收数据。

}

1.2.2 BeginReceiveEndReceive 接收连接的数据

public class ServerStateObject  //MSDN自定义的类,用于Socket通信

{

public Socket workSocket = null;

public const int BUFFER_SIZE = 1024 * 4;

public byte[] buffer = new byte[BUFFER_SIZE];

}

//与接受回调方法一样,读取回调方法也是一个AsyncCallback委托。该方法将来自客户端套接字的一个或多个字节读入数据缓冲区,然后再次调用BeginReceive方法,直到客户端发送的数据完成为止。从客户端读取整个消息后,在控制台上显示字符串,并关闭处理与客户端的连接的服务器套接字。

public static void ReadCallback(IAsyncResult ar)

{

String content = String.Empty;

//创建自定义的状态对象

ServerStateObject state = (ServerStateObject)ar.AsyncState;

Socket handler = state.workSocket;//处理的句柄

//读出

int bytesRead = handler.EndReceive(ar);//结束挂起的异步读取。

if(bytesRead>0)

{

String len = Encoding.UTF8.GetBytes(result).Length.ToString().PadLeft(8,’0′);

String result=Encoding.ASCII.GetString(state.buffer,0,bytesRead);

Send(len + result, handler);

}

};

state.buffer,0,bytesRead)

1.2.3 BeginSendEndSend发送数据

private static void Send(string data, Socket handler)

{

byte[] byteData = Encoding.UTF8.GetBytes(data);

handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);//将数据异步发送到连接的Socket

}

private static void SendCallback(IAsyncResult ar)

{

Socket handler = (Socket)ar.AsyncState;

//向远端发送数据

int bytesSent = handler.EndSend(ar);//结束挂起的异步发送。

StateObject state = new StateObject();

state.workSocket = handler;

handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback),state);

handler.Shutdown(SocketShutdown.Both);

handler.Close();

}

public static void StopListening()

{

ServerAcceptDone.Close();


}

TCP客户端异步连接、接收数据、发送数据

BeginConnect:开始一个对远程主机连接的异步请求。主机由主机名和端口号指定。

EndConnect:结束挂起的异步连接请求。

IPEndPoint RemoteEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);

ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

ClientSocket.BeginConnect(RemoteEndPoint, new AsyncCallback(AsyConnectServerCallback), ClientSocket);//启动连接

//客户端 异步连接回调函数

private void AsyConnectServerCallback(IAsyncResult ar)

{

Socket ClientSocket = (Socket)ar.AsyncState;

string ErrorInfo;

try

{

ClientSocket.EndConnect(ar);

ClientConnectedFlag = true;

}

catch (SocketException ex){

ClientConnectedFlag = false;

switch (ex.ErrorCode)

{

ClientConnectedFlag = false;

switch (ex.ErrorCode)

{

case 10060://连接超时

case 10061://由于目标机器积极拒绝,无法连接

break;

default:

ErrorInfo = "AsyConnectServerCallback:" + ex.ToString();

DebugOutput.ProcessMessage(ErrorInfo);//输出到文件

break;

}

}

//向BLL层推送连接处理信息

if (ServerConnectedEvent != null)

ServerConnectedEvent(ClientSocket, ClientConnectedFlag);

if (ClientConnectedFlag == true)

ClientReceiveData(); //开始异步接受数据,此处会阻塞

}


//客户端 异步接收数据回调函数

private void ClientReceiveDataCallback(IAsyncResult ar)

{

string ErrorInfo;

try

{

if (ClientConnectedFlag == true)

{

ClientStateObject ClientReceiveState = (ClientStateObject)ar.AsyncState;

Socket TempReceiveSocket = ClientReceiveState.workSocket;

if (TempReceiveSocket != null && TempReceiveSocket.Connected)

{

int ClientReceiveDataSize = TempReceiveSocket.EndReceive(ar);

if (ClientReceiveDataSize > 0)

{

if (ClientRecStrEvent != null)

{

ClientRecStrEvent(TempReceiveSocket, ClientReceiveState.buffer, ClientReceiveDataSize);//推送到上一层。

}

ClientReciveDone.Set();

TempReceiveSocket.BeginReceive(ClientReceiveState.buffer, 0, ClientStateObject.BUFFER_SIZE, 0, new AsyncCallback(ClientReceiveDataCallback), ClientReceiveState);

ClientReciveDone.WaitOne();

}

else

{//远程服务器关闭通知

CloseClient();//关闭连接

//向BLL层推送连接处理信息

if (ServerConnectedEvent != null)

ServerConnectedEvent(ClientSocket, ClientConnectedFlag);

}

}

}

}

catch (SocketException ex)

{

ErrorInfo = String.Format("ClientReceiveDataCallback:[{0}] {1}", ex.ErrorCode, ex.ToString());

DebugOutput.ProcessMessage(ErrorInfo);//输出到文件

if (ex.ErrorCode == 10054)

{

CloseClient();//关闭连接

//向BLL层推送连接处理信息

if (ServerConnectedEvent != null)

ServerConnectedEvent(ClientSocket, ClientConnectedFlag);

}

}

}



详情参考:https://msdn.microsoft.com/zh-cn/library/6y0e13d3(v=vs.80).aspx

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容