参考:https://blog.csdn.net/jia12216/article/details/82702960
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
单线程:一个任务从头至尾由一线程完成,在一个任务完成之前,不接受第二个任务。
多线程 ,:将一个任务拆分成不同的阶段(部分),并交给不同的 线程 分别处理。
什么是同步,什么是异步
异步:和同步相对,同步是顺序执行,而异步是彼此独立,在等待某个事件的过程中继续做自己的事,不要等待这一事件完成后再工作。线程是实现异步的一个方式,异步是让调用方法的主线程不需要同步等待另一个线程的完成,从而让主线程干其他事情。
我们把在完成了一次调用后通过状态、通知和回调来告知调用者的方式称为异步过程。
在异步过程中当调用一个方法时,调用者并不能够立刻得到结果,只有当这个方法调用完毕后调用者才能获得调用结果。这样做的好处是什么呢?答案是高效。
以数据传输为例:不必等待该数据传输完成并返回socket通信结果,直接继续传输。一个 顾客一边等待配置、一边做些别的事情,就是多线程了。
异步和多线程:不是同等关系,异步是目的,多线程只是实现异步的一个手段,实现异步可以采用多线程技术或者交给其他进程来处理。
显而易见,异步 可以提供更高的效率,它可以利用 等待 的时间去完成一些事情。
代码参考https://gameinstitute.qq.com/community/detail/119099
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace AsyncServer
{
public class AsyncTCPServer
{
public void Start()
{
//创建套接字
IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定端口和IP
socket.Bind(ipe);
//设置监听
socket.Listen(10);
//连接客户端
AsyncAccept(socket);
}
///
/// 连接到客户端
///
///
private void AsyncAccept(Socket socket)
{
socket.BeginAccept(asyncResult =>
{
//获取客户端套接字
Socket client = socket.EndAccept(asyncResult);
Console.WriteLine(string.Format("客户端{0}请求连接...", client.RemoteEndPoint));
AsyncSend(client, "服务器收到连接请求");
AsyncSend(client, string.Format("欢迎你{0}",client.RemoteEndPoint));
AsyncReveive(client);
}, null);
}
///
/// 接收消息
///
///
private void AsyncReveive(Socket socket)
{
byte[] data = new byte[1024];
try
{
//开始接收消息
socket.BeginReceive(data, 0, data.Length, SocketFlags.None,
asyncResult =>
{
int length = socket.EndReceive(asyncResult);
Console.WriteLine(string.Format("客户端发送消息:{0}", Encoding.UTF8.GetString(data)));
}, null);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
///
/// 发送消息
///
///
///
private void AsyncSend(Socket client, string p)
{
if (client == null || p == string.Empty) return;
//数据转码
byte[] data = new byte[1024];
data = Encoding.UTF8.GetBytes(p);
try
{
//开始发送消息
client.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
{
//完成消息发送
int length = client.EndSend(asyncResult);
//输出消息
Console.WriteLine(string.Format("服务器发出消息:{0}", p));
}, null);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
参考 https://blog.csdn.net/freesundark/article/details/5565069
参数:一个委托。一个对象。对象包含此请求的状态信息。
其中回调方法中必须使用EndAccept方法。
应用程序调用BeginAccept方法后,系统会使用单独的线程执行指定的回调方法并在EndAccept上一直处于阻塞状态,直至监测到挂起的链接。EndAccept会返回新的socket对象供你来同远程主机数据交互。返回的socket即为Client
public IAsyncResult BeginSend(byte[] buffer, int offset,int size,SocketFlags socketFlags, AsyncCallback callback,object state);
Buffer表示要发送的数据,offset表示buffer中发送数据的位置,size为发送字节数的大小,socketFlags指SocketFlags值的按位组合。
public socket(AddressFamily 寻址类型, SocketType 套接字类型, ProtocolType 协议类型)
但需要注意的是套接字类型与协议类型并不是可以随便组合。
从上表中可以看出,这些方法都是成对出现的。这些方法能避免网络通信中的阻塞现象。这些方法的使用机制是在Begin开头的方法中注册一个回调函数,当对应的事件发生时,调用该回调函数,且在回调函数中调用对应的End开头的方法。
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace AsyncClient
{
public class AsyncTCPClient
{
///
/// 连接到服务器
///
public void AsynConnect()
{
//端口及IP
IPEndPoint ipe = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6065);
//创建套接字
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//开始连接到服务器
client.BeginConnect(ipe, asyncResult =>
{
client.EndConnect(asyncResult);
//向服务器发送消息
AsynSend(client,"你好我是客户端");
AsynSend(client, "第一条消息");
AsynSend(client, "第二条消息");
//接受消息
AsynRecive(client);
}, null);
}
///
/// 发送消息
///
///
///
public void AsynSend(Socket socket, string message)
{
if (socket == null || message == string.Empty) return;
//编码
byte[] data = Encoding.UTF8.GetBytes(message);
try
{
socket.BeginSend(data, 0, data.Length, SocketFlags.None, asyncResult =>
{
//完成发送消息
int length = socket.EndSend(asyncResult);
Console.WriteLine(string.Format("客户端发送消息:{0}", message));
}, null);
}
catch (Exception ex)
{
Console.WriteLine("异常信息:{0}", ex.Message);
}
}
///
/// 接收消息
///
///
public void AsynRecive(Socket socket)
{
byte[] data = new byte[1024];
try
{
//开始接收数据
socket.BeginReceive(data, 0, data.Length, SocketFlags.None,
asyncResult =>
{
int length = socket.EndReceive(asyncResult);
Console.WriteLine(string.Format("收到服务器消息:{0}", Encoding.UTF8.GetString(data)));
AsynRecive(socket);
}, null);
}
catch (Exception ex)
{
Console.WriteLine("异常信息:", ex.Message);
}
}
}
}
参考https://blog.csdn.net/qq_40544338/article/details/103599590
lambda表达式,表示一个匿名函数,=>前面的是参数,后面的是函数体。你可以把它当作一个函数。