大家好,关于套接字编程教程很多朋友都还不太明白,今天小编就来为大家分享关于的知识,希望对各位有所帮助!
3. 套接字.Listen(10);
4.
5. 同时(正确)
6.{
7. //阻塞直到接受新客户端。
8. var client=socket.Accept();
9.
10. //创建一个新线程来接收来自客户端的消息。
11. Task.Factory.StartNew(()=
12.{
13. var buffer=新字节[1024];
14. 同时(正确)
15.{
16. //阻塞直到收到消息。
17. int len=client.Receive(buffer);
18.
19. //.
20.}
21。 });
22。 }
这是最简单的阻塞Socket服务器。服务器套接字将在Accept 方法中被阻塞,直到连接客户端套接字连接。
Receive方法是阻塞方法,因此需要启动一个线程循环来监听数据并处理数据。
该方法性能有限,无法满足高并发、高容量、高时效场景。
2. 简单的异步套接字服务器。
1. var socket=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
3. 套接字.Listen(10);
4.
5.socket.BeginAccept(发送者=
6.{
7. var client=(Socket)sender.AsyncState;
8. var buffer=新字节[1024];
9. client.BeginReceive(buffer, 0, 1024, SocketFlags.None, sender1=
10.{
11. //做某事。
12. //调用再次开始接收。
14. },客户端);
15.
16. //再次调用开始接受。
17. },套接字);
3. 高I/O 异步套接字服务器。
2.
3. var socket=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
4.socket.Bind(端点);
5. 套接字.Listen(100);
6.
7.varacceptAsyncProxy=new SocketAsyncEventArgs
8.{
10. UserToken=套接字
11.};
12.acceptAsyncProxy.Completed +=(sender, e)=
13.{
14. var client=(Socket)sender;
15. var buffer=新字节[1024];
16. var receiveAsyncProxy=new SocketAsyncEventArgs
17.{
19. UserToken=客户端
20.};
21. receiveAsyncProxy.SetBuffer(缓冲区, 0, 1024);
22. receiveAsyncProxy.Completed +=(sender1, e1)=
23。 {
24. var buf=e1.Buffer;
25.
26. //做某事。
27. 客户端.ReceiveAsync(e1);
28. };
29. client.ReceiveAsync(receiveAsyncProxy);
30.
31.socket.AcceptAsync(e);
32.};
33.socket.AcceptAsync(acceptAsyncProxy);
当我们能够预测并发量,有针对性地预先分配SAEA实例,并在发送和接收数据时复用它们时,服务器的性能就能得到很大的提升。
通常,我们会为每个客户端Socket分配一个接收和发送SAEA实例,并根据需要预先分配几个SAEA实例来接收客户端Socket链接。
4. 增强
在使用SAEA的基础上,可以进行进一步的优化。
主要优化点在于接收和发送的缓冲区。在大量数据发送和接收时,如果频繁创建缓冲区用于接收或发送,将会造成巨大的开销、内存溢出甚至宕机。
下面分别介绍接收和发送。
4.1 接收器
根据业务需要,我们可以预先分配一个总缓冲区,以便每个SAEA都可以分配并使用其中的一块区域。当客户端Socket断开连接时,我们认为之前由SAEA持有的区域被释放,并且可以在下一个客户端Socket连接时使用。
1. _bufferManager=new BufferManager(bufferSize * MaxConnectionNumber, bufferSize);
2. //预分配SocketAsyncEventArgs 对象池
3. SocketAsyncEventArgs socketEventArg;
4.
5. var socketArgsProxyList=new ListSocketAsyncEventArgsProxy(MaxConnectionNumber);
6.
7. for (int i=0; i 最大连接数; i++)
8.{
9. //预先分配一组可重用的SocketAsyncEventArgs
10.socketEventArg=new SocketAsyncEventArgs();
11. _bufferManager.SetBuffer(socketEventArg);
12.
13.socketArgsProxyList.Add(new SocketAsyncEventArgsProxy(socketEventArg));
14.}
通常情况下,我们根据预设的客户端Socket连接数来设置总缓冲区大小,每个客户端Socket分配的缓冲区大小需要根据实际业务进行调整。
上述代码中的Buffer Manager类管理所有接收SAEA的缓冲区。
4.2 发件人
同样,我们也会为每个SAEA分配一个发送队列(Sending Queue,以下简称SQ),用于维护发送数据,遵循先进先出的规则。
1. if (_sendingQueuePool==null)
2. _sendingQueuePool=((SocketServerBase)((ISocketServerAccessor)appSession.AppServer).SocketServer).SendingQueuePool;
3.
4.SendingQueue队列;
5. if (_sendingQueuePool.TryGet(出队列))
6.{
7. _sendingQueue=队列;
8. 队列.StartEnqueue();
9. }
但有一点不同的是,考虑到吞吐量因素,发送的数据需要在发送前进行管理。我们需要创建一个Sending Queue Pool(以下简称SP)来管理SQ。
通常,每个SAEA 都拥有一个SQ 发送队列。当吞吐量较大且当前SQ被SAEA占用时,后续需要发送的数据需要创建新的SQ进行维护。在当前数据发送之前,数据由新的SQ管理。
1.发送队列newQueue;
2.
3. if (!_sendingQueuePool.TryGet(out newQueue))
4.{
6. AppSession.Logger.Error(‘没有足够的发送队列可供使用。’);
7.退货;
8.}
9.
10. var oldQueue=Interlocked.CompareExchange(ref _sendingQueue, newQueue, 队列);
11.
12. if (!ReferenceEquals(oldQueue, 队列))
13.{
14. if (newQueue !=null)
15. _sendingQueuePool.Push(newQueue);
16.
17. if (IsInClosingOrClosed)
18.{
20.}
21.其他
22。 {
24. AppSession.Logger.Error(‘切换发送队列失败。’);
25.}
26.
27.返回;
28.}
29.
30. //开始允许入队
31. newQueue.StartEnqueue();
32. 队列.StopEnqueue();
值得注意的是,为了防止发送的数据被篡改,当前的SQ被置于只读状态。 SP不能无限制地创建新的SQ。当超过最大SQ数量时,将返回失败。因为此时服务器已经达到了瓶颈,继续创建只会不断地将数据写入内存,严重时可能会导致宕机。
当一个SAEA持有多个SQ时,发送重用后需要回收。
1. //初始化SocketAsyncEventArgs用于发送
2. _socketEventArgSend=new SocketAsyncEventArgs();
3. _socketEventArgSend.Completed +=new EventHandlerSocketAsyncEventArgs(OnSendingCompleted);
4.
5. _socketEventArgSend.UserToken=队列;
6.
7. if (队列.计数1)
8. _socketEventArgSend.BufferList=队列;
9.其他
10.{
11. var item=队列[0];
12. _socketEventArgSend.SetBuffer(item.Array, item.Offset, item.Count);
13.}
14.
15. var client=客户端;
16.
17. if (客户端==空)
18.{
19. OnSendError(队列, CloseReason.SocketError);
20.返回;
21。 }
22。
23. if (!client.SendAsync(_socketEventArgSend))
24. OnSendingCompleted(客户端, _socketEventArgSend);
无论发送是否成功,发送完成后我们都需要调用回调函数。在回调函数中,如果此时有数据要发送,则会再次获取客户端Socket对应的SQ,并再次调用发送方法。这样做是为了递归地发送数据。
4. 增强 奖励
进一步优化。
更深入的理解请参考Buffer Manage、Sending Queue、Smart Pool的实现。
4.1 接收器
1)与发送方不同,接收方需要考虑分包因素。因此需要设置一个协议让服务器知道是否收到了数据包。这里只是一些想法。
最简单的方法是使用特殊的分隔符作为数据结束的指示符;更安全的方法是在第一个数据包中使用特殊的请求头来声明数据包的长度。您还可以向每个数据包添加请求标头和序列号。更安全的是,您可以在每个数据包的末尾添加Crc 和其他检查。当然,后两种方法无疑会带来额外的开销。
2)与发送端类似,当数据较大时,需要分包。与SQ的原理类似,接收到的数据被缓存,处理后释放。
4.2 发件人
1)建议为最大客户端连接数预先分配预计SQ数的1/6;最多可维持2倍连接数的SQ,即每个SAEA可容纳1个正在发送的SQ和1个等待发送的SQ。不断收集发送数据的SQ。 SQ多了不利于维护,利用率低。
2)再次注意,SQ数据发送后需要释放,其维护的数据集合会被清除。如果当前有等待的SQ,则需要将当前正在执行的SQ回收并返回给SP重新使用。
3)设置单个SQ的数据容量上限。发送更多的数据会花费很长的时间,这可能会导致服务器上发送的数据积压和内存溢出。
4) 重复使用相同的数据。当一条数据需要推送到所有客户端Socket时,重复为其打开一个区域的开销是非常大的,所以使用时需要注意。
4.3 架构
如图所示,这只是一个应用架构,可以更好的隔离Socket的维护部分,专注于业务开发。
Socket Session维护Socket,初始化时向SP申请SQ,通过SAEA实现高性能的接收和发送方法。
Socket Server是一个Socket监控服务。当客户端Socket连接时,会创建对应的Socket Session,分配数据缓冲区和数据接收所需的SAEA,并通知App Server。
App Session是Socket Session在业务层面的实现。不需要关注Socket。提供发送方法和数据过滤方法。当一条数据被处理时,App Server 会收到通知。
Receive Filter类是一个具体的数据处理类,根据自定义协议转换为基本数据类型Request Info。
Receive Filter Factory是创建Receive Filter的工厂方法类。针对不同的业务场景创建特定的接收过滤器。
App Server是核心枢纽,它创建和维护Socket Server并维护所有App Session。创建App Session时,将Socket Session与其绑定,并通过指定的Receive Filter Factory创建对应的过滤器Receive Filter。当收到App Session处理的基础数据Request Info时,根据其属性Key加载对应的Command。
原创文章,作者:小su,如若转载,请注明出处:https://www.sudun.com/ask/116469.html
用户评论
像从了良
终于找到一篇讲清楚 Socket 的教程了!以前一直觉得网络编程很蒙,看了这篇感觉好点。特别是讲解异步编程那部分,写得深入浅出!
有10位网友表示赞同!
黑夜漫长
我正在学嵌入式开发,需要用到 Socket 通信。这篇文章正好解决了我的问题。简单实用,内容涵盖面广,学习起来很顺畅。
有10位网友表示赞同!
青山暮雪
Socket 套接字编程真的非常复杂,看了很多教程都没能完全理解。这篇文章讲解的比较详细,把重点都交代清楚了,感觉像是在身边指导自己一样!
有8位网友表示赞同!
安之若素
作为一名后端开发工程师,基础 Socket 知识还是比较了解的。不过这篇教程讲解的更加深入,让我对高性能网络编程有了新的认识。
有8位网友表示赞同!
陌颜幽梦
我觉得这篇文章缺乏实际案例,只停留在理论层面,没有办法真正帮助我理解 Socket 如何应用于实际项目中。
有16位网友表示赞同!
妄灸
代码注释太少,有些地方看不太明白,希望能加一些详细的解释和示例,以便更好地学习和理解。
有8位网友表示赞同!
反正是我
学习 Socket 编程真的不容易,感觉这篇文章也没讲到太多实用的东西。建议作者可以多添加一些实际项目案例,让读者更容易入门。
有7位网友表示赞同!
何年何念
这篇教程虽然写得不错,但是太基础了,对于有一定经验的开发人员来说显得有点浅薄。希望作者能深入讲解一些更高级的 Socket 编程技术,例如高并发处理、网络安全等。
有7位网友表示赞同!
我怕疼别碰我伤口
我很喜欢这篇文章的文字风格,语言简洁易懂,而且结构清晰易循。对于初学者来说非常友好!
有19位网友表示赞同!
凝残月
Socket 套接字编程确实是一个比较重要的知识点,这篇文章讲解得也很到位,我收获了不少新知识! 但我觉得这个教程缺少了对常见问题和错误处理的讨论,如果能补充这些内容,将会更加完善一些。
有12位网友表示赞同!
寻鱼水之欢
感觉这篇文章偏重于技术细节,对于初学者来说可能有点 overwhelming ,建议可以将一些更复杂的理论概念分解成更容易理解的步骤。
有6位网友表示赞同!
该用户已上天
对Socket编程还是比较感兴趣,但这篇教程让我觉得有点枯燥乏味,希望能加入更多生动的案例或动画演示,让学习过程更加趣味性!
有13位网友表示赞同!
经典的对白
我一直在学网络安全,Socket 知识对于我的学习非常重要。这篇文章讲解了 Socket 通信的原理和协议,帮助我更好地理解了网络攻击是如何发生的。
有8位网友表示赞同!
←极§速
Socket 套接字编程确实是一个非常强大的工具,可以用来实现多种远程通信应用。这篇教程让我对 Socket 的可能性有了更清晰的认识!
有7位网友表示赞同!
剑已封鞘
这篇文章对我来说有点过于深入,我希望能看到一些具体的代码示例,这样更容易理解和记忆。
有8位网友表示赞同!
歇火
学习了这篇关于Socket套接字编程的教程后,我对网络编程有了更深刻的理解。感谢作者分享这些宝贵的知识!
有10位网友表示赞同!
命里缺他
这个教程对我来说太基础了,我已经学过很多 Socket 的知识,希望作者能提供一些更高级的教程内容。
有16位网友表示赞同!
罪歌
这篇文章对Socket 套接字编程的讲解非常详细,涵盖了大部分重要的概念和技术。强烈推荐给有兴趣学习网络编程的人!
有19位网友表示赞同!