C#网络通信Socket详解

  最近在做一个联机的双人对战网络游戏,看教程之后对于Socket仍然一知半解,查完资料之后,就明白了,分享给大家,有错误欢迎指出,留言讨论。

一、准备  

协议分为TCP和UDP。

TCP:传输控制协议

  • TCP是面向连接的、可靠的
  • TCP是基于字节流的传输层协议

UDP:用户数据报协议

  • UDP与TCP相反,是无连接的、不可靠的协议
  • UDP是基于数据报的传输(因其不可靠传输效率比TCP高) 

我们采用TCP来实现B-S通信。

如上图,一次完整的Socket连接的流程,其中Socket是指套接字,套接字是支持TCP/IP协议网络通信的基本单元。

  1. 开启一个连接之前,建立Socket连接
  2. Bind使Socket与一个本地终结结点相关联,就是绑定IP和端口号
  3. Listen开始监听,参数是监听几个客户端,0是不受限制。

主线程和线程池:非主线程不能设置Unity3d的组件

当客户端使用BeginReceive实现异步接收的时候,因为C#使用线程池处理异步调用,所以ReceiveCb并不在主线程中,所以采用中介者模式来调用,只有主线程能设置(Satrt() Update()属性主线程)UI组件,所以在ReceiveCb方法中只设置传递的字符,具体在主线程里云处理。

 

非主线程无法设置unity中的组件:

多线程转换成单线程,由于C#中的异步通信由线程池实现,BeginReceive属于多线程,所以需要通过中介在主线程之中处理消息。

服务器端

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading.Tasks;

 

namespace Servece

{

class Program

{

static void Main(string[] args)

{

//搭建 Socket

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

IPAddress ip = IPAddress.Parse("192.168.1.14");

IPEndPoint ipPoint = new IPEndPoint(ip, 1234);

 

 

//Bind

serverSocket.Bind(ipPoint);//建立连接

 

//Listen

serverSocket.Listen(0);//监听

//Socket clientSocket = serverSocket.Accept(); 只能接收一个客户端

//Accept

serverSocket.BeginAccept(AcceptCall, serverSocket);

Console.ReadKey();//很重要,不然会报错。作用:等待键盘输入,退出程序,使调试时能看到输出结果。如果没有此句,命令窗口会一闪而过。

 

}

static void AcceptCall(IAsyncResult ar) {

 

Socket severSocket = ar.AsyncState as Socket;

//给客户端发送信息

Socket clientSocket =severSocket.EndAccept(ar);

string msg = "你已连上服务器";

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

clientSocket.Send(data);

 

//接收客户端发来的信息

dataBuffer = new byte[1024];

 

clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReciveClient, clientSocket);

severSocket.BeginAccept(AcceptCall, severSocket);

 

}

static byte[] dataBuffer=new byte[1024];

static void ReciveClient(IAsyncResult ar)

{

Socket clientSocket = null;

//用try来处理客户端突然关闭的情况

try

{

clientSocket = ar.AsyncState as Socket;

 

int count = clientSocket.EndReceive(ar);

//当没有传过来信息的时候,关闭客户端

if (count == 0) {

 

clientSocket.Close();

return;

}

 

string msg = Encoding.UTF8.GetString(dataBuffer, 0, count);

Console.WriteLine("从客户端接收到消息:" + msg);

 

clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReciveClient, clientSocket);

}

catch (Exception e)

{

 

clientSocket.Close();

Console.WriteLine(e);

}

 

}

}

}

 

 

Socket serverSocket = new

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

AdddressFamily:

InterNetwork

使用IPv4

InterNetworkV6

使用IPv6

SocketType

Stream

字节流(支持可靠、双向、基于连接的字节流)

Dgram

支持数据报、无连接、不可靠的数据报类型。

BeginReceive参数说明

buffer

Byte类型的数组,用于存储接收到的数据

offset

buffer参数中存储数据的位置,该位置从零开始计数

size

最大字节数

socketFlags

SocketFlags值的按位组合,这里设置为0

callback

回调函数,一个AsyncCallback委托

state

一个用户定义对象,其中包含接收操作的相关信息,当操作完成时,该对象会传递给EndReceive委托

Accept:

当开始监听之后,服务器调用Accept(0接收客户端的连接,若没有客户端连接,将会调用阻塞方法,也就是当没有客户端连接的时候,程序卡在Accept()不会往下执行,直到接收到客户端的连接。返回值是一个client(新客户端的Socket) 。

Receive:

阻塞方法,和Accept一样,参数为byte[]类型存储接收的数据,返回值为接收到数据的长度。

Send:

接受一个byte[]类型的参数指明要发送的内容。返回值指明发送数据的长度,服务器将字符串转换成byte[]类型发送给客户端

 

异步Socket:实现多个客户端。

BeginAccept

参数

说明

asyncCallBack

回调函数

state

表示状态信息,必须保证state中包含socket句柄

 

posted @ 2018-03-08 14:16  薛小爽  阅读(1583)  评论(0编辑  收藏  举报