文档库 最新最全的文档下载
当前位置:文档库 › C# 实现的多线程异步Socket数据包接收器框架

C# 实现的多线程异步Socket数据包接收器框架

C# 实现的多线程异步Socket数据包接收器框架
C# 实现的多线程异步Socket数据包接收器框架

C# 实现的多线程异步Socket数据包接收器框架

分类:C#2009-03-19 18:44 3608人阅读评论(2) 收藏举报

几天前在博问中看到一个C# Socket问题,就想到笔者2004年做的一个省级交通流量接收服务器项目,当时的基本求如下:

?接收自动观测设备通过无线网卡、Internet和Socket上报的交通量数据包

?全年365*24运行的自动观测设备5分钟上报一次观测数据,每笔记录约2K 大小

?规划全省将有100个左右的自动观测设备(截止2008年10月还只有30个)当时,VS2003才发布年多,笔者也是接触C#不久。于是Google了国内国外网,希望找点应用C#解决Socket通信问题的思路和代码。最后,找到了两篇帮助最大的文章:一篇是国人写的Socket接收器框架,应用了独立的客户端Socket会话(Session)概念,给笔者提供了一个接收服务器的总体框架思路;另一篇是美国人写的,提出了多线程、分段接收数据包的技术方案,描述了多线程、异步Socket的许多实现细节,该文坚定了笔者采用多线程和异步方式处理Socket接收器的技术

路线。

具体实现和测试时笔者还发现,在Internet环境下的Socket应用中,需要系统有极强的容错能力:没有办法控制异常,就必须允许它们存在(附加源代码中可以看到,try{}catch{}语句较多)。对此,笔者设计了一个专门的检查和清理线程,完成无效或超时会话的清除和资源释放工作。

依稀记得,国内框架作者的名称空间有ibm,认为是IBM公司职员,通过邮件

后才知道其人在深圳。笔者向他请教了几个问题,相互探讨了几个技术关键点。可惜,现在再去找,已经查不到原文和邮件了。只好借此机会,将本文献给这两个素未谋

面的技术高人和同行,也盼望拙文或源码能给读者一点有用的启发和帮助。

1、主要技术思路

整个系统由三个核心线程组成,并由.NET线程池统一管理:

?侦听客户端连接请求线程:ListenClientRequest(),循环侦听客户端连接请求。

如果有,检测该客户端IP,看是否是同一观测设备,然后建立一个客户端

TSession对象,并通过Socket异步调用方法BeginReceive()接收数据包、

EndReceive()处理数据包

?数据包处理线程:HandleDatagrams(),循环检测数据包队列_datagramQueue,完成数据包解析、判断类型、存储等工作

?客户端状态检测线程:CheckClientState(),循环检查客户端会话表_sessionTable,判断会话对象是否有效,设置超时会话关闭标志,清楚无效

会话对象及释放其资源

2、主要类简介

系统主要由3个类组成:

?TDatagramReceiver(数据包接收服务器):系统的核心进程类,建立Socket 连接、处理与存储数据包、清理系统资源,该类提供全部的public属性和方法

?TSession(客户端会话):由每个客户端的Socket对象组成,有自己的数据缓冲区,清理线程根据该对象的最近会话时间判断是否超时?TDatagram(数据包类):判断数据包类别、解析数据包

3、关键函数和代码

下面简介核心类TDatagramReceiver的关键实现代码。

3.1 系统启动

系统启动方法StartReceiver()首先清理资源、创建数据库连接、初始化若干计数值,然后创建服务器端侦听Socket对象,最后调用静态方法

ThreadPool.QueueUserWorkItem()在线程池中创建3个核心处理线程。

///

///启动接收器

///

public bool StartReceiver()

{

try

{

_stopReceiver = true;

this.Close();

if (!this.ConnectDatabase()) return false;

_clientCount = 0;

_datagramQueueCount = 0;

_datagramCount = 0;

_errorDatagramCount = 0;

_exceptionCount = 0;

_sessionTable = new Hashtable(_maxAllowClientCount);

_datagramQueue = new Queue(_maxAllowDatagramQueueCount);

_stopReceiver = false; // 循环中均要该标志

if (!this.CreateReceiverSocket()) //建立服务器端 Socket 对象

{

return false;

}

// 侦听客户端连接请求线程, 使用委托推断, 不建 CallBack 对象if (!ThreadPool.QueueUserWorkItem(ListenClientRequest))

{

return false;

}

// 处理数据包队列线程

if (!ThreadPool.QueueUserWorkItem(HandleDatagrams))

{

return false;

}

// 检查客户会话状态, 长时间未通信则清除该对象

if (!ThreadPool.QueueUserWorkItem(CheckClientState))

{

return false;

}

_stopConnectRequest = false; // 启动接收器,则自动允许连接

}

catch

{

this.OnReceiverException();

_stopReceiver = true;

}

return !_stopReceiver;

}

下面是创建侦听Socket对象的方法代码。

///

///创建接收服务器的 Socket, 并侦听客户端连接请求

///

private bool CreateReceiverSocket()

{

try

{

_receiverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, Pr otocolType.Tcp);

_receiverSocket.Bind(new IPEndPoint(IPAddress.Any, _tcpSocketPort)); // 绑定端口

_receiverSocket.Listen(_maxAllowListenQueueLength); // 开始监听

return true;

}

catch

{

this.OnReceiverException();

return false;

}

}

3.2 侦听客户端连接请求

服务器端循环等待客户端连接请求。一旦有请求,先判断客户端连接数是否超限,接着检测该客户端IP地址,一切正常后建立TSession对象,并调用异步方法接收客户端Socket数据包。

代码中,Socket读到数据时的回调AsyncCallback委托方法EndReceiveData()完成数据接收工作,正常情况下启动另一个异步BeginReceive()调用。

.NET中,每个异步方法都有自己的独立线程,异步处理其实也基于多线程机制的。下面代码中的异步套异步调用,既占用较大的系统资源,也给处理带来意想不到的结果,更是出现异常时难以控制和处理的关键所在。

///

///循环侦听客户端请求,由于要用线程池,故带一个参数

///

private void ListenClientRequest(object state)

{

Socket client = null;

while (!_stopReceiver)

{

if (_stopConnectRequest) // 停止客户端连接请求

{

if (_receiverSocket != null)

{

try

{

_receiverSocket.Close(); // 强制关闭接收器

}

catch

{

this.OnReceiverException();

}

finally

{

// 必须为 null,否则 disposed 对象仍然存在,将引发下面的错误 _receiverSocket = null;

}

}

continue;

}

else

{

if (_receiverSocket == null)

{

if (!this.CreateReceiverSocket())

{

continue;

}

}

}

try

{

if (_receiverSocket.Poll(_loopWaitTime, SelectMode.SelectRead))

{

// 频繁关闭、启动时,这里容易产生错误(提示套接字只能有一个) client = _receiverSocket.Accept();

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

{

if (this._clientCount >= this._maxAllowClientCount)

{

this.OnReceiverException();

try

{

client.Shutdown(SocketShutdown.Both);

client.Close();

}

catch { }

}

else if (CheckSameClientIP(client)) // 已存在该 IP 地址

{

try

{

client.Shutdown(SocketShutdown.Both);

client.Close();

}

catch { }

}

else

{

TSession session = new TSession(client);

session.LoginTime = DateTime.Now;

lock (_sessionTable)

{

int preSessionID = session.ID;

while (true)

{

if (_sessionTable.ContainsKey(session.ID)) // 有可能重复该编号 {

session.ID = 100000 + preSessionID;

}

else

{

break;

}

}

_sessionTable.Add(session.ID, session); // 登记该会话客户端

Interlocked.Increment(ref _clientCount);

}

this.OnClientRequest();

try// 客户端连续连接或连接后立即断开,易在该处产生错误,系统忽略之

{

// 开始接受来自该客户端的数据

session.ClientSocket.BeginReceive(session.ReceiveBuffer, 0,

session.ReceiveBufferLength, SocketFlags.None, EndReceiveData, s ession);

}

catch

{

session.DisconnectType = TDisconnectType.Exception;

session.State = TSessionState.NoReply;

}

}

}

else if (client != null) // 非空,但没有连接(connected is false)

{

try

{

client.Shutdown(SocketShutdown.Both);

client.Close();

}

catch { }

}

}

}

catch

{

this.OnReceiverException();

if (client != null)

{

try

{

client.Shutdown(SocketShutdown.Both); client.Close();

}

catch { }

}

}

// 该处可以适当暂停若干毫秒

}

// 该处可以适当暂停若干毫秒

}

3.3 处理数据包

该线程循环查看数据包队列,完成数据包的解析与存储等工作。具体实现时,如果队列中没有数据包,可以考虑等待若干毫秒,提高CPU利用率。

private void HandleDatagrams(object state)

{

while (!_stopReceiver)

{

this.HandleOneDatagram(); // 处理一个数据包

if (!_stopReceiver)

{

// 如果连接关闭,则重新建立,可容许几个连接错误出现

if (_sqlConnection.State == ConnectionState.Closed)

{

this.OnReceiverWork();

try

{

_sqlConnection.Open();

}

catch

{

this.OnReceiverException();

}

}

}

}

}

///

///处理一个包数据,包括:验证、存储

///

private void HandleOneDatagram()

{

TDatagram datagram = null;

lock (_datagramQueue)

{

if (_datagramQueue.Count > 0)

{

datagram = _datagramQueue.Dequeue(); // 取队列数据 Interlocked.Decrement(ref _datagramQueueCount);

}

}

if (datagram == null) return;

datagram.Clear();

datagram = null; // 释放对象

}

3.4 检查与清理会话

本线程负责处理建立连接后的客户端会话TSession或Socket对象的关闭与资源清理工作,其它方法中出现异常等情况,尽可能标记相关TSession对象的属性NoReply=true,表示该会话已经无效、需要清理。

检查会话队列并清理资源分3步:第一步,Shutdown()客户端Socket,此时可能立即触发某些Socket的异步方法EndReceive();第二步,Close()客户端Socket,释放占用资源;第三步,从会话表中清除该会话对象。其中,第一步完成后,某个TSession也许不会立即到第二步,因为可能需要处理其异步结束方法。

需要指出,由于涉及多线程处理,需要频繁加解锁操作,清理工作前先建立一个会话队列列副本sessionTable2,检查与清理该队副本列列的TSession对象。

///

///检查客户端状态(扫描方式,若长时间无数据,则断开)

///

private void CheckClientState(object state)

{

while (!_stopReceiver)

{

DateTime thisTime = DateTime.Now;

// 建立一个副本,然后对副本进行操作

Hashtable sessionTable2 = new Hashtable();

lock (_sessionTable)

{

foreach (TSession session in _sessionTable.Values)

{

if (session != null)

{

sessionTable2.Add(session.ID, session);

}

}

}

foreach (TSession session in sessionTable2.Values) // 对副本进行操作

{

Monitor.Enter(session);

try

{

if (session.State == TSessionState.NoReply) // 分三步清除一个 Session {

session.State = TSessionState.Closing;

if (session.ClientSocket != null)

{

try

{

// 第一步:shutdown

session.ClientSocket.Shutdown(SocketShutdown.Both);

}

catch { }

}

}

else if (session.State == TSessionState.Closing)

{

session.State = TSessionState.Closed;

if (session.ClientSocket != null)

{

try

{

// 第二步: Close

session.ClientSocket.Close();

}

catch { }

}

}

else if (session.State == TSessionState.Closed)

{

lock (_sessionTable)

{

// 第三步:remove from table

_sessionTable.Remove(session.ID);

Interlocked.Decrement(ref _clientCount);

}

this.OnClientRequest();

session.Clear(); // 清空缓冲区

}

else if (session.State == TSessionState.Normal) // 正常的会话

{

TimeSpan ts = thisTime.Subtract(https://www.wendangku.net/doc/9418304312.html,stDataReceivedTime);

if (Math.Abs(ts.TotalSeconds) > _maxSocketDataTimeout) // 超时,则准备断开连接

{

session.DisconnectType = TDisconnectType.Timeout;

session.State = TSessionState.NoReply; // 标记为将关闭、准备断开

}

}

}

finally

{

Monitor.Exit(session);

}

} // end foreach

sessionTable2.Clear();

} // end while

}

4 、结语

基于多线程处理的系统代价是比较大的,需要经常调用加/解锁方法lock()或Monitor.Enter(),需要经常创建处理线程等。从实际运行效果看,笔者的实现方案有较好的稳定性:2005年4月到5月间,在一个普通PC机器上连续运行30多天不出一点故障。同时,笔者采用了时序区间判重等算法,有效地提高了系统处理与响

应速度。测试表明,在普通的PC机器(P4 2.0)上,可以做到0.5秒处理一个数据包,如果优化代码和服务器,还有较大的性能提升空间。

上面的代码是笔者实现的省级公路交通流量数据服务中心(DSC)项目中的接收服务器框架部分,整个系统还包括:数据转发交通部的转发服务器、数据远程查询客户端、综合报表数据处理系统、数据在线发布系统、系统运行监控系统等。

实际的接收服务器类及其辅助类超过3K行,整个系统则超过了60K。因为是早期实现的程序,难免有代码粗糙、方法欠妥的感觉,只有留待下个版本完善扩充了。由于与甲方有保密合同和版权保护等,不可能公开全部源代码,删减也有不当之处,读者发现时请不吝指正。下面是带详细注释的代码下载URL。

?下载框架源码

附注:笔者补充了有关数据包界限、间断、重叠等内容,请参考指正。

Tag标签: 技术

C# 实现的多线程异步Socket数据包接收器框架(补记)

国庆假日的最后一天,用近9个小时写完了C# 实现的多线程异步Socket数据包接收器框架(包括删减代码的时间)。饭后散步回来再看,好家伙,有300多个Page Views了,超过笔者在codeproject上首日前几个小时的PV速度了。呵呵,如果发表在笔者原博客网上,估计就是自己反反复复修改记录的数十个PV了!终究是彼网牛人高手太多。

散步时仔细想想该文,发觉有三个Socket通信中关键与著名的问题没有讲到或没有讲清楚:

?数据包界限符问题。根据原项目中交通部标准,在连续观测站中数据包中,使用<>两个字符表示有效数据包开始和结束。实际项目有各自的具体技术规范

?数据包不连续问题。在TCP/IP等通信中,由于时延等原因,一个数据包被Socket做两次或多次接收,此时在接收第一个包后,必须保存到TSession的DatagramBuffer中,在以后一并处理

?包并发与重叠问题。由于客户端发送过快或设备故障等原因,一次接收到一个半、两个或多个包文。此时,也需要处理、一个半、两个或多个包

先补充异步BeginReceive()回调函数EndReceiveData()中的数据包分合函数ResolveBuffer()。

///

/// 1) 报文界限字符为<>,其它为合法字符,

/// 2) 按报文头、界限标志抽取报文,可能合并包文

/// 3) 如果一次收完数据,此时 DatagramBuffer 为空

/// 4) 否则转存到包文缓冲区 session.DatagramBuffer

///

private void ResolveBuffer(TSession session, int receivedSize)

{

// 上次留下的报文缓冲区非空(注意:必然含有开始字符 <,空时不含 <)

bool hasBeginChar = (session.DatagramBufferLength > 0);

int packPos = 0; // ReceiveBuffer 缓冲区中包的开始位置

int packLen = 0; // 已经解析的接收缓冲区大小

byte dataByte = 0; // 缓冲区字节

int subIndex = 0; // 缓冲区下标

while (subIndex < receivedSize)

{

// 接收缓冲区数据,要与报文缓冲区 session.DatagramBuffer 同时考虑

dataByte = session.ReceiveBuffer[subIndex];

if (dataByte == TDatagram.BeginChar) // 是数据包的开始字符<,则前面的包文均要放弃

{

// <前面有非空串(包括报文缓冲区),则前面是错包文,防止 AAA 两个报文一次读现象

if (packLen > 0)

{

Interlocked.Increment(ref _datagramCount); // 前面有非空字符

Interlocked.Increment(ref _errorDatagramCount); // 一个错误包

this.OnDatagramError();

}

session.ClearDatagramBuffer(); // 清空会话缓冲区,开始一个新包

packPos = subIndex; // 新包起点,即<所在位置

packLen = 1; // 新包的长度(即<)

hasBeginChar = true; // 新包有开始字符

}

else if (dataByte == TDatagram.EndChar) // 数据包的结束字符 >

{

if (hasBeginChar) // 两个缓冲区中有开始字符<

{

++packLen; // 长度包括结束字符>

数据库死锁问题总结

数据库死锁问题总结 1、死锁(Deadlock) 所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造 成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系 统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力 协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象 死锁。一种情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每 个线程都在等待被其他线程占用并堵塞了的资源。例如,如果线程A锁住了记 录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发 生了死锁现象。计算机系统中,如果系统的资源分配策略不当,更常见的可能是 程序员写的程序有错误等,则会导致进程因竞争资源不当而产生死锁的现象。 锁有多种实现方式,比如意向锁,共享-排他锁,锁表,树形协议,时间戳协 议等等。锁还有多种粒度,比如可以在表上加锁,也可以在记录上加锁。(回滚 一个,让另一个进程顺利进行) 产生死锁的原因主要是: (1)系统资源不足。 (2)进程运行推进的顺序不合适。 (3)资源分配不当等。 如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能 性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序 与速度不同,也可能产生死锁。 产生死锁的四个必要条件: (1)互斥条件:一个资源每次只能被一个进程使用。 (2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 破解:静态分配(分配全部资源) (3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 破解:可剥夺 (4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 破解:有序分配 这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。 死锁的预防和解除:

WebSocket协议的握手和数据帧

WebSocket协议的握手和数据帧 WebSocket是定义服务器和客户端如何通过Web通信的一种网络协议。协议是通信的议定规则。组成互联网的协议组由IETF(互联网工程任务组)发布。IETF发布评议请求(Request for Comments,RFC),精确地规定了协议(包括RFC 6455):WebSocket协议。RFC 6455于2011年12月发布,包含了实现WebSocket客户端和服务器时必须遵循的规则。 websocket基本上是一个很简单的协议, 主要流程非常少, 实现起来也很简单。 为简单起见, 下面只分析握手和数据帧的报文. 一. 握手(handshake). 握手协议由客户端发起, 服务器响应, 一来一回就完成了. 基本上是为了兼容现有的http 基础设施. 下面是一个客户端发起的握手请求: 47 45 54 20 2F 20 48 54 54 50 2F 31 2E 31 0D 0A GET./.HTTP/1.1.. 55 70 67 72 61 64 65 3A 20 77 65 62 73 6F 63 6B Upgrade:.websock 65 74 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 et..Connection:. 55 70 67 72 61 64 65 0D 0A 48 6F 73 74 3A 20 31 Upgrade..Host:.1 39 32 2E 31 36 38 2E 38 2E 31 32 38 3A 31 33 30 92.168.8.128:130 30 0D 0A 4F 72 69 67 69 6E 3A 20 6E 75 6C 6C 0D 0..Origin:.null. 0A 50 72 61 67 6D 61 3A 20 6E 6F 2D 63 61 63 68 .Pragma:.no-cach 65 0D 0A 43 61 63 68 65 2D 43 6F 6E 74 72 6F 6C e..Cache-Control 3A 20 6E 6F 2D 63 61 63 68 65 0D 0A 53 65 63 2D :.no-cache..Sec- 57 65 62 53 6F 63 6B 65 74 2D 4B 65 79 3A 20 64 WebSocket-Key:.d 33 35 39 46 64 6F 36 6F 6D 79 71 66 78 79 59 46 359Fdo6omyqfxyYF 37 59 61 63 77 3D 3D 0D 0A 53 65 63 2D 57 65 62 7Yacw==..Sec-Web 53 6F 63 6B 65 74 2D 56 65 72 73 69 6F 6E 3A 20 Socket-Version:. 31 33 0D 0A 53 65 63 2D 57 65 62 53 6F 63 6B 65 13..Sec-WebSocke 74 2D 45 78 74 65 6E 73 69 6F 6E 73 3A 20 78 2D t-Extensions:.x- 77 65 62 6B 69 74 2D 64 65 66 6C 61 74 65 2D 66 webkit-deflate-f 72 61 6D 65 0D 0A 55 73 65 72 2D 41 67 65 6E 74 https://www.wendangku.net/doc/9418304312.html,er-Agent 3A 20 4D 6F 7A 69 6C 6C 61 2F 35 2E 30 20 28 57 :.Mozilla/5.0.(W 69 6E 64 6F 77 73 20 4E 54 20 36 2E 31 3B 20 57 indows.NT.6.1;.W 4F 57 36 34 29 20 41 70 70 6C 65 57 65 62 4B 69 OW64).AppleWebKi 74 2F 35 33 37 2E 33 36 20 28 4B 48 54 4D 4C 2C t/537.36.(KHTML, 20 6C 69 6B 65 20 47 65 63 6B 6F 29 20 43 68 72 .like.Gecko).Chr 6F 6D 65 2F 33 32 2E 30 2E 31 36 35 33 2E 30 20 ome/32.0.1653.0. 53 61 66 61 72 69 2F 35 33 37 2E 33 36 0D 0A 0D Safari/537.36... 0A

Java Socket网络编程--聊天室的实现(多线程实现无需等待对方响应版本)

一,服务端: package com.samael.socket; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import https://www.wendangku.net/doc/9418304312.html,.ServerSocket; import https://www.wendangku.net/doc/9418304312.html,.Socket; public class MyServer { class ThGetMsg extends Thread{ @Override public void run() { if(fromClient==null) return; while(true){ try { System.out.println("Message From Client:["+fromClient.readLine()+"]"); this.sleep(50); } catch (IOException e) { break; } catch (InterruptedException e) { e.printStackTrace(); } } } } public static BufferedReader fromClient=null; public static void main(String[] args) { try { //get server socket ServerSocket server=new ServerSocket(3166); //get socket from client Socket socket=server.accept(); System.out.println("The IP["+socket.getInetAddress()+"] is connected!");

socket编程实现客户端和服务器端通信

#include "" #include <> #include #pragma comment(lib,"") #define BUF_SIZE 64 int _tmain(int argc,_TCHAR* argv[]) { WSADATA wsd; S OCKET sServer; S OCKET SClient; i nt retVal; c har buf[BUF_SIZE]; i f (WSAStartup(MAKEWORD(2,2),&wsd)!=0) {printf("wsastartup failed!\n"); return 1; } s Server=socket(AF_INET,SOCK_STREAM,IPPROTO_TC P); i f (INVALID_SOCKET==sServer) {printf("socket failed!\n"); WSACleanup(); return -1; } S OCKADDR_IN addrServ; =AF_INET; =htons(9990); retVal=bind(sServer,(const struct sockaddr*) &addrServ,sizeof(SOCKADDR_IN)); i f (SOCKET_ERROR==retVal) {printf("bind failed!\n"); closesocket(sServer); WSACleanup(); return -1; } retVal=listen(sServer,1); i f (SOCKET_ERROR==retVal) {printf("listen failed!\n"); closesocket(sServer); WSACleanup(); return -1; } p rintf("tcp server start...\n"); s ockaddr_in addrClient; i nt addrClientlen=sizeof(addrClient); S Client=accept(sServer,(sockaddr FAR*)&addrClient,&addrClientlen); i f (INVALID_SOCKET==SClient) { printf("accept failed!\n"); closesocket(sServer); WSACleanup(); return -1; } w hile(true) { ZeroMemory(buf,BUF_SIZE); retVal=recv(SClient,buf,BUF_SIZE,0); if (SOCKET_ERROR==retVal) { printf("recv failed!\n"); closesocket(sServer); closesocket(SClient); WSACleanup(); return -1; } SYSTEMTIME st; GetLocalTime(&st); char sDataTime[30]; sprintf(sDataTime,"%4d-%2d-%2d %2d:%2d:%2d",, ,,,,; printf("%s,recv from client [%s:%d]:%s\n",sDataTime,inet_ntoa,,buf); if (StrCmp(buf,"quit")==0) { retVal=send(SClient,"quit",strlen("quit"),0); break; } else { char msg[BUF_SIZE]; sprintf(msg,"message received -%s",buf); retVal=send(SClient,msg,strlen(msg),0); if (SOCKET_ERROR==retVal) { printf("send failed!\n"); closesocket(sServer); closesocket(SClient); WSACleanup(); return -1; } } } c losesocket(sServer); c losesocket(SClient);

Socket通信协议

一.登录 指令:SocketCommand.LoginCommand.Login 参数:用户名+“|”+密码 二.登录成功 指令:SocketCommand.LoginCommand.LoginOk 参数:服务器下当前账号所拥有的的摄像头数量+“|”+上次登录时间+“|”+上次登录IP 说明:登录成功会同时返回服务器上该账号下摄像头的数量,请与本地摄像头数量进行比对,然后再发起上传或者下载的指令 三.登录失败 指令:SocketCommand.LoginCommand.LoginError 参数:登录失败原因,一般为“用户名或者密码错误!” 说明:收到此指令,可直接用弹出窗口显示参数内容提醒用户,用户重新输入用户名密码后再重新发起登录指令 四.创建数据连接 指令:SocketCommand.NormalCommand.CreatDataSocket 参数:用户名+“|”+密码 说明:申请创建数据传输专用连接,主要是为了在高峰期或者数据量大的情况下同步摄像头数据而不会影响到主端口通讯 五.返回数据端口 指令:SocketCommand.NormalCommand. ReturnDataSocketPort 参数:数据传输端口 说明:服务端针对CreatDataSocket指令所返回的结果,当服务端目前没有可用端口的时候参数会返回空值,请注意判断,如果参数不为空,可针对此端口发起socket短 连接,此连接不需要保持心跳包,不需要验证身份 六.上传摄像头 指令:SocketCommand.CaramCommand.UploadCaramer 参数:摄像头ID+“|”+摄像头密码+“|”+当前数量+“|”+总数量 说明:登录成功后如果判断到本地的摄像头数量大于服务器上的摄像头数量,就可以立即发起创建数据连接指令,然后根据返回的端口成功创建数据连接之后,就可以 发起该指令了,一次只上传一个摄像头,第三个参数默认从1开始 七.上传摄像头成功 指令:SocketCommand.CaramCommand. UploadSucess 参数:摄像头ID+“|”+摄像头在线状态+“|”+已同步数量+“|”+总数量 说明:当已同步数量等于总数量的时候,就可以关闭连接了,关闭连接不需要通知服务端 八.上传摄像头失败 指令:SocketCommand.CaramCommand. UploadFail 参数:摄像头ID+“|”+失败原因+“|”+已同步数量+“|”+总数量 说明:失败原因有以下几个值:(1)ID和密码不匹配(2)该摄像头绑定账号已满九.下载摄像头 指令:SocketCommand.CaramCommand.DownLoadCaramer 参数:已下载的摄像头数量 说明:同上传摄像头的说明

C 中Socket多线程编程实例

C 中Socket多线程编程实例 C#中Socket多线程编程实例2010年07月18日星期日10:58 P.M.C#是 微软随着https://www.wendangku.net/doc/9418304312.html,新推出的一门语言。它作为一门新兴的语言,有着C++的强健,又有着VB等的RAD特性。而且,微软推出C#主要的目的是为了对抗Sun公司 的Java。大家都知道Java语言的强大功能,尤其在网络编程方面。于是,C# 在网络编程方面也自然不甘落后于人。本文就向大家介绍一下C#下实现套接字(Sockets)编程的一些基本知识,以期能使大家对此有个大致了解。首先,我向大家介绍一下套接字的概念。 套接字基本概念: 套接字是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。可以将套接字看作不同主机间的进程进行双向通信的端点,它构成了单个主机内 及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的 线程通过套接字通信而引进的一种抽象概念。套接字通常和同一个域中的套接 字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。 套接字可以根据通信性质分类,这种性质对于用户是可见的。应用程序一 般仅在同一类的套接字间进行通信。不过只要底层的通信协议允许,不同类型 的套接字间也照样可以通信。套接字有两种不同的类型:流套接字和数据报套 接字。 套接字工作原理: 要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户机端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。 根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过 程可以分为三个步骤:服务器监听,客户端请求,连接确认。

数据库原理作业

数据库原理作业 客观题预算成绩:100 分 题号:1 题型:单选题(请在以下几个选项中选择唯一正确答案) 在关系数据库设计中,设计关系模式是( )的任务 A、A)需求分析阶段 B、B)概念设计阶段 C、C)逻辑设计阶段 D、D)物理设计阶段 学员答案:C 正确性:正确 题号:2 题型:单选题(请在以下几个选项中选择唯一正确答案) 存取方法设计是数据库设计的( )阶段的任务。 A、一个1:1联系可以转换为一个独立的关系模式,也可以与联系的任意一端实体所对应的关系模式合并 B、一个1:n联系可以转换为一个独立的关系模式,也可以与联系的n端实体所对应的关系模式合并 C、一个m:n联系可以转换为一个独立的关系模式,也可以与联系的任意一端实体所对应的关系模式合并 D、三个或三个以上的实体间的多元联系转换为一个关系模式 学员答案:C 正确性:正确 题号:3 题型:单选题(请在以下几个选项中选择唯一正确答案) 数据流图属于数据库设计的哪个阶段的工具?( ) A、需求分析阶段 B、概念结构设计阶段 C、逻辑结构设计阶段 D、物理结构设计阶段 学员答案:A 正确性:正确 题号:4 题型:单选题(请在以下几个选项中选择唯一正确答案) 在数据库设计中,将ER图转换成关系数据模型的过程属于( )。 A、需求分析阶段 B、逻辑设计阶段 C、概念设计阶段 D、物理设计阶段 学员答案:B

正确性:正确 题号:5 题型:单选题(请在以下几个选项中选择唯一正确答案) 在数据库设计的需求分析阶段,描述数据与处理之间关系的方法是() A、ER图 B、业务流程图 C、数据流图 D、程序框图 学员答案:C 正确性:正确 题号:6 题型:单选题(请在以下几个选项中选择唯一正确答案) 建立索引属于数据库的() A、概念设计 B、逻辑设计 C、物理设计 D、实现与维护设计 学员答案:C 正确性:正确 题号:7 题型:单选题(请在以下几个选项中选择唯一正确答案) 在关系数据库设计中,设计关系模式是____的任务。 A、需求分析阶段 B、概念设计阶段 C、逻辑设计阶段 D、物理设计阶段 学员答案:C 正确性:正确 题号:8 题型:单选题(请在以下几个选项中选择唯一正确答案) 从E-R模型向关系模式转换时,一个m:n联系转换为关系模式时,该关系模式的码是 A、A)m端实体的码 B、B)n端实体的码 C、C)m端实体码与n端实体码的组合 D、D)重新选取其他属性 学员答案:C 正确性:正确 题号:9 题型:多选题(请在复选框中打勾,在以下几个选项中选择正确答案,答案可以

WebSocket协议中文版

Internet Engineering Task Force (IETF) I. Fette Request for Comments: 6455 Google, Inc. Category: Standards Track A. Melnikov ISSN: 2070-1721 Isode Ltd. December 2011 张开涛[译] WebSocket协议

摘要 WebSocket协议实现在受控环境中运行不受信任代码的一个客户端到一个从该代码已经选择加入通信的远程主机之间的全双工通信。用于这个的安全模型是通常由web浏览器使用的基于来源的安全模型。该协议包括一个打开阶段握手、接着是基本消息帧、TCP之上的分层(layered over TCP)。该技术的目标是为需要与服务器全双工通信且不需要依赖打开多个HTTP连接(例如,使用XMLHttpRequest或