谁能给个rails+websocket4net 例子的简单应用例子

Ruby on Rails,一对一关联(One-to-One)
场景1:一对一关联一般用于一个东西A有且只有一个B,比如
&&& 雇员 has_one:电脑
&&& 教室 has_one:老师
他们的特点是,对象之间存在唯一的拥有关系。如果一个雇员也可以有多台电脑,不过这就不是今天的话题了,那是一对多关系(One-to-Many)。
场景2:将一张表的内容分离成两张(多张)表,比如
&&& 病人 has_one:住院档案
&&& 顾客 has_one: 账单地址
他们的特点是,本来这些信息可以放在同一个表中,通过不同的列表示属性。不过为了逻辑更为清晰,将比较独立的属性从主表中拆分出来单独存储。还有一个好处就是,好多情况下主表(病人)信息被访问的几率较大,而从表(住院档案)属性很多并且查询几率较少时,将一个大表分离为多个表可以提高使用效率。
不过说实话,实际应用中依然是较少有机会使用一对一关联,取而代之的应该是一对多关联。
举个例子,一般来说一个顾客只有一个账单地址。但是貌似应该也有多个账单地址的情况。建立对象关系的时候还是要考虑使用一对多关联以应对这种变化。
有两点值得注意,以A has B = B belongs to A为例
第1:A是主表,B是从表。
第2:A中id的主键值,在B中作为关联用的外键值。
第3:一般来说需要在A、B类中定义对方的引用,以便能够通过一方找到另一方。
从头开始创建教室和老师模型类
rails g model Teacher class_room_id:integer name:string&
rails g model ClassRoom name:string&
自动生成了迁移任务。执行rake db:migrate命令让数据库结构生效后可以看出,并没有建立class_room_id的外键依赖关系,和我们通常设计数据库的方式不太一样是不是。
因为Rails 社区习惯不依赖数据库而只做模型层关联。当然了,如果认为应用环境需要数据库层的关联校验,也可以加上数据库外键。
#创建老师表的迁移任务&
class CreateTeachers & ActiveRecord::Migration&
& def change&
&&& create_table :teachers do |t|&
&&&&& t.integer :class_room_id&
&&&&& t.string :name&
&&&&& t.timestamps&
#创建教室表的迁移任务&
class CreateClassRooms & ActiveRecord::Migration&
& def change&
&&& create_table :class_rooms do |t|&
&&&&& t.string :name&
&&&&& t.timestamps&
在Teacher模型和ClassRoom模型中分别添加belongs_to和has_one。这样的声明,建立了ClassRoom和Teacher的一对一关联。
Rails神奇的地方来了,belongs_to后面写的是class_has_one后面写的是teacher。这种符合人类语言习惯的声明会被惯例的转换为外键依赖,teacher会自动根据class_room_id的值找到所属于的class_room;class_room也会根据teacher表中的id知道它拥有的teacher。如果想显示的设置或者不遵守Rails的约定命名也是可以的,只需要通过:foreign_key=&&class_room_id&指定外键(如注释掉的那行一样)。
#Teacher模型类&
class Teacher & ActiveRecord::Base&
& belongs_to :class_room&
& #belongs_to :class_room , {:foreign_key =& &class_room_id&}&
& attr_accessible :class_room_id, :name&
#ClassRoom模型类&
class ClassRoom & ActiveRecord::Base&&
& has_one :teacher&&
& attr_accessible :name&
用rails console看看效果,创建一个class_room,教室名字是101。
& class_room = ClassRoom.create(:name=&'101')&
=& #&ClassRoom id: 1, name: &101&, created_at: & 17:34:07&, updated_at: & 17:34:07&&&
在创建一个teacher,名字是&abbuggy&。回显内容是teacher的当前状态,class_room_id现在还是nil。
& teacher = Teacher.create(:name=&'abbuggy')&
=& #&Teacher id: 1, class_room_id: nil, name: &abbuggy&, created_at: & 17:36:05&, updated_at: & 17:36:05&&&
因为在Teacher模型类中定义了belongs_to :class_room关系,所以teacher对象能够调用teacher.class_room=方法;同样在class_room对象上能够调用teacher=方法,其实这已经证明类的关联关系已经建立了。彼此关联起来后,再次查看,已经成功建立了对象引用关系。
& class_room.teacher = teacher&
& teacher.class_room = class_room&
& class_room.teacher&
=& #&Teacher id: 1, class_room_id: 1, name: &abbuggy&, created_at: & 17:36:05&, updated_at: & 17:37:05&&&&
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'WebSocket的JavaScript例子 - Javascript - 炫意HTML5
&nbsp> &nbsp&nbsp> &nbsp
| 14309 次浏览
详细解读一个简单html5 WebSocket的Js实例教程,附带完整的javascript websocket实例源码,以及实例代码效果演示页面,并对本实例的核心代码进行了深入解读。从WebSocket通讯三个阶段(打开握手、数据传递、关闭握手)进行了探讨,各阶段中浏览器和服务器做了些什么事情也有所涉及。
一个html5 WebSocket + JS的简单Echo例子,例子代码演示效果猛戳链接:(打开页面,稍等一会)
使用一个文本编辑器,把下面代码复制保存在一个 websocket.html 文件中,然后只要在浏览器中打开它,页面就会使用 websocket 自动连接,发送一个消息,显示接受到的服务器响应,然后关闭连接。
&!DOCTYPE html&
&meta charset="utf-8" /&
&title&WebSocket Test&/title&
&script language="javascript"type="text/javascript"&
var wsUri ="ws://echo.websocket.org/";
function init() {
output = document.getElementById("output");
testWebSocket();
function testWebSocket() {
websocket = new WebSocket(wsUri);
websocket.onopen = function(evt) {
onOpen(evt)
websocket.onclose = function(evt) {
onClose(evt)
websocket.onmessage = function(evt) {
onMessage(evt)
websocket.onerror = function(evt) {
onError(evt)
function onOpen(evt) {
writeToScreen("CONNECTED");
doSend("WebSocket rocks");
function onClose(evt) {
writeToScreen("DISCONNECTED");
function onMessage(evt) {
writeToScreen('&span style="color:"&RESPONSE: '+ evt.data+'&/span&');
websocket.close();
function onError(evt) {
writeToScreen('&span style="color:"&ERROR:&/span& '+ evt.data);
function doSend(message) {
writeToScreen("SENT: " + message);
websocket.send(message);
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML =
output.appendChild(pre);
window.addEventListener("load", init, false);
&h2&WebSocket Test&/h2&
&div id="output"&&/div&
主要代码解读:
申请一个WebSocket对象,参数是需要连接的服务器端的地址,同http协议使用http://开头一样,WebSocket协议的URL使用ws://开头,另外安全的WebSocket协议使用wss://开头。。
var wsUri ="ws://echo.websocket.org/";
websocket = new WebSocket(wsUri);
WebSocket对象一共支持四个消息 onopen, onmessage, onclose和onerror,
我们可以看出所有的操作都是采用消息的方式触发的,这样就不会阻塞UI,使得UI有更快的响应时间,得到更好的用户体验。
当Browser和WebSocketServer连接成功后,会触发onopen消息;
websocket.onopen = function(evt) {
如果连接失败,发送、接收数据失败或者处理数据出现错误,browser会触发onerror消息;
websocket.onerror = function(evt) {
当Browser接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含server传输过来的数据;
websocket.onmessage = function(evt) {
当Browser接收到WebSocketServer端发送的关闭连接请求时,就会触发onclose消息。
websocket.onclose = function(evt) {
WebSocket与TCP、HTTP的关系WebSocket与http协议一样都是基于TCP的,所以他们都是可靠的协议,Web开发者调用的WebSocket的send函数在browser的实现中最终都是通过TCP的系统接口进行传输的。
WebSocket和Http协议一样都属于应用层的协议,那么他们之间有没有什么关系呢?答案是肯定的,WebSocket在建立握手连接时,数据是通过http协议传输的,但是在建立连接之后,真正的数据传输阶段是不需要http协议参与的。
推荐另外一篇文章
WebSocket通讯详细解读:
从下图可以明显的看到,分三个阶段:
下图显示了WebSocket主要的三步 浏览器和 服务器端分别做了那些事情。
WebSocket的优点
a)、服务器与客户端之间交换的标头信息很小,大概只有2字节;
b)、客户端与服务器都可以主动传送数据给对方;
c)、不用频率创建TCP请求及销毁请求,减少网络带宽资源的占用,同时也节省服务器资源;
建立连接的握手
当Web应用程序调用new WebSocket(url)接口时,Browser就开始了与地址为url的WebServer建立握手连接的过程。
1. Browser与WebSocket服务器通过TCP三次握手建立连接,如果这个建立连接失败,那么后面的过程就不会执行,Web应用程序将收到错误消息通知。
2. 在TCP建立连接成功后,Browser/UA通过http协议传送WebSocket支持的版本号,协议的字版本号,原始地址,主机地址等等一些列字段给服务器端。
3. WebSocket服务器收到Browser/UA发送来的握手请求后,如果数据包数据和格式正确,客户端和服务器端的协议版本号匹配等等,就接受本次握手连接,并给出相应的数据回复,同样回复的数据包也是采用http协议传输。
4. Browser收到服务器回复的数据包后,如果数据包内容、格式都没有问题的话,就表示本次连接成功,触发onopen消息,此时Web开发者就可以在此时通过send接口想服务器发送数据。否则,握手连接失败,Web应用程序会收到onerror消息,并且能知道连接失败的原因。
这个握手很像HTTP,但是实际上却不是,它允许服务器以HTTP的方式解释一部分handshake的请求,然后切换为websocket
WebScoket协议中,数据以帧序列的形式传输。
考虑到数据安全性,客户端向服务器传输的数据帧必须进行掩码处理。服务器若接收到未经过掩码处理的数据帧,则必须主动关闭连接。
服务器向客户端传输的数据帧一定不能进行掩码处理。客户端若接收到经过掩码处理的数据帧,则必须主动关闭连接。
针对上情况,发现错误的一方可向对方发送close帧(状态码是1002,表示协议错误),以关闭连接。
关闭WebSocket(握手)
使用Wireshark监控到的上面WebSocket例子的数据。
GET / HTTP/1.1
  Upgrade: websocket
  Connection: Upgrade
  Host: echo.websocket.org
  Origin: null
  Pragma: no-cache
  Cache-Control: no-cache
  Sec-WebSocket-Key: Qcgtb1RJ6HceeTRLPFux/A==
  Sec-WebSocket-Version: 13
  Sec-WebSocket-Extensions: x-webkit-deflate-frame
  Cookie: __utma=; __utmc=9925811; __utmz=.1.utmcsr=websocket.org|utmccn=(referral)|utmcmd=referral|utmcct=/
  HTTP/1.1 101 Web Socket Protocol Handshake
  Upgrade: WebSocket
  Connection: Upgrade
  Sec-WebSocket-Accept: 84Qpane33QhxOmcz8bGkFdE1AHk=
  Server: Kaazing Gateway
  Date: Tue, 16 Apr :25 GMT
  Access-Control-Allow-Origin: null
  Access-Control-Allow-Credentials: true
  Access-Control-Allow-Headers: content-type
  Access-Control-Allow-Headers: authorization
  Access-Control-Allow-Headers: x-websocket-extensions
  Access-Control-Allow-Headers: x-websocket-version
  Access-Control-Allow-Headers: x-websocket-protocol
  J6&h..8a/.{x%.0y..WebSocket rocks..i.....
14309 3375 2349 1870 1626 1454 1146 956 679 492 413 猜你感兴趣转载原文: 作者:JasonRuby on Rails是一个非常流行的开源web框架,它基于ruby语言,致力于精简web开发流程。Rails基于“约定优于配置“(convention over configuration)的原则, 简而言之,在默认情况下,Rails假设你遵循了“标准”的约定(像命名规则,代码结构等等),如果你这样做了,很多事情会自动完成而不需要关心细节。 这样做虽然有优点,但同时也存在一些陷阱。最显著的问题就是:这种隐藏在框架之后的“魔法”通常会带来困惑,你经常会问:为什么会这样?同时, 它也带来了安全和性能问题。因此,Rails虽然易于使用,但也很容易被滥用。本文聚焦于10个最常见的rails陷进,并指导你如何避免它们以及由它们引发的问题。常见错误1: 将太多的逻辑放在controllerRails基于MVC架构。 在Rails社区里,一直以来我们谈论的都是fat model,skinny controller,然而在最近的几个rails项目中,我打破了这个原则, 因为将视图层逻辑或模型层逻辑放入控制器中实在是轻而易举。问题是在于,一旦控制器对象违反单一责任原则,未来修改代码将变得困难且容易出错。总的来说,放在controller中的逻辑应该有:Session 和 cookie 处理。 这可能包括authentication/authorization或任何你需要的cookie操作;选择model。 根据请求中所带的参数找到正确的model对象。理想情况下,调用用一个带有实例变量的find方法,然后响应;请求参数管理。 收集请求参数,并且调用一个合适的model方法去持久化它们;渲染结果(html、xml、json等)或重定向虽然单一责任原则增加了局限性,但这是rails框架要求在控制器中所做的最低限度的事情。个人认为,将逻辑放在controller最大的坏处是不利于测试。模型层的测试用例是最好写的,你只需要关心你构造的数据和逻辑代码产生的结果是否一致,为控制器写测试用例很容易受其他因素的干扰,比如路由规则,测试结果依赖控制层的方法和请求的参数等等。所以有很多的rails开源项目并没有为控制器逻辑写测试用例,而且控制器中的代码应该精简到不需要写测试用例。常见错误2: 将太多的逻辑放在viewRails模版引擎-ERB是构建可变内容页面的良好方式。可是如果你不够细心的话,你将很快被一大堆难以管理和维护的,并且糅合了大量html和ruby代码的文件所 包围,这也是导致大量代码重复和违背DRY原则的地方。在视图层中过度使用条件逻辑就是违背该原则的其中之一,一个简单的例子:current_user方法可以返回当前登录的用户,通常情况下,代码中会出现这样的条件逻辑结构:&h3&& Welcome,& &% if current_user %&& & &%= current_user.name %&& &% else %&& & Guest& &% end %&&/h3&其实有更好的方法来处理这样的问题,那就是无论用户是否登录,都确保current_user返回的对象总是存在。例如,你也许在app/controllers/application_controller.rb定义了一个current_user帮助函数:require 'ostruct'helper_method :current_userdef current_user& @current_user ||= User.find session[:user_id] if session[:user_id]& if @current_user& & @current_user& else& & OpenStruct.new(name: 'Guest')& endend你可以用一行代码代替之前的代码:&h3&Welcome, &%= current_user.name =%&&/h3&一些其他的建议:适当的使用view layouts and partials去封装页面中重复的东西;使用像Draper gem这样的装饰者,在一个ruby对象中去封装构建视图层的逻辑,然后将方法添加到这个对象中去执行逻辑操作。常见错误3: 将太多的逻辑放在model鉴于将最少的逻辑放在view和controller的指导思想,在MVC的架构之下,只有把所有的逻辑放在model下面么?其实,并不是这样。许多的开发人员经常犯这样一个错误,他们坚持将一切东西都放在ActiveRecord的模型类下面,这不仅违反了单一职责原则,也带了了维护的噩梦。像生成邮件通知,连接外部服务,数据格式转换这样的功能,它们并不像ActiveRecord这样肩负核心的责任,而ActiveRecord应该多做一些数据查找和持久化数据的事情。因此,如果逻辑不应该放在view,不应该放在controller,也不应该放在model,那么它应该放在哪里呢?像rails这样的综合框架,初级开发者通常不愿意在框架之外创建自己的类。然而,将逻辑从model移到普通ruby对象中正合我意,这可以避免过于复杂的模型。 通过ruby普通对象,你可以把邮件通知或API接口放在它们自己的类中,而不是放在ActiveRecord模型中。总的来说,model中应该保留的逻辑有:ActiveRecord configuration (例如关联和验证);封装字段更新以及把其保存到数据库的方法;访问隐藏内部模型信息的包装器(例如:一个full_name方法是数据库中first_name和last_name字段的结合);复杂的查询(比find更复杂的查询)。总的来说,除了model类本身之外,你不应该在其他地方使用where或其他类似的查询。常见错误4: 把通用的helper类当作垃圾场这个错误是以上错误的一种推论。如前所述,Rails框架重视指定的MVC框架组件(例如model,view,controller),每一个组件的类都有定义好的东西, 但有些时候,我们需要的一些方法并不适合放在这三个组件中。Rails生成器方便地构建了一个helper目录和一个新的helper class去对应我们创建的每个新资源。它太诱人了,可以把任何不适合放在view,model和controller的功能放在它们对应的helper类中。Rails确实是MVC-centric,没有什么阻止你去创建自己类并添加适当的目录来保存这些类的代码。当你有额外的功能,考虑哪些方法需要组在一起,并为类取个好名字去保存这些方法的时候, 使用Rails这样全面的框架并不表示你要放弃良好的面向对象设计原则。常见错误5: 使用太多的gem包Rails被一个丰富的gems生态系统所支持,并且它提供给所有的开发人员使用,对于快速构建复杂应用,这相当的棒!我也见过许多gem包数量庞大的应用程序,但其Gemfile中的gem包和它所提供的功能并不成正比。这会导致几个问题:过度的使用gem包会使rails的体积过大,这降低了在生产环境下的性能。除了用户体验变差以外,也增加了内存消耗和操作成本。rails应用越大,相应的启动时间就会越长,这不仅降低了开发效率,也使自动化测试耗费更多时间。请记住,每个带进应用程序的gem包都可能依赖于其他的gem包,反过来,这些gem包又可能依赖于别的,添加其他的gems会造成复合影响。例如,添加一个rails_admin包会连带安装其他的11个gem包,这会导致rails安装时间延长10%。在撰写本文时,如果安装一个新的Rails4.1.0,在gemfile.lock文件中会包含43个gem包,这明显的要比Gemfile中定义的的要多,这表明,每个gem包都会有相关的依赖。当你添加每一个gem包的时候,请仔细的考虑是否值得这样做。很多的开发者经常会添加rails_admin,只是因为它可以给model结构提供一个漂亮的web前端,但这真的不比一个数据库浏览工具更好。即使你的应用需要一个有额外权限的管理员用户,你可能并不想给他原始数据库的访问权限,开发更合理的管理功能通常比添加这些gem包要好。常见错误6: 忽略日志文件虽然许多的开发者意识到rails在开发和生产环境下会提供默认的日志文件,但这些文件中的信息并没有引起足够的重视。即使许多的应用在生产环境下依赖于像Honeybadger和New Relic这样的日志监测工具,但在开发和测试过程中,重视你的日志文件也很重要。像之前提到的,rails框架为你提供了很多的“魔法”,特别是在model中,定义模型之间的关联变得非常容易,所有模块对象的SQL语句都自动为你生成,这很棒!但是你知道这些被生成的SQL语句的效率么?经常会遇见的问题就是N+1查询,虽然这个问题已经相当的明了,但是发现他们的有效途径就是在日志文件中观察SQL查询。例如,在一个典型的博客应用中,查询要显示一些文章的评论:def comments_for_top_three_posts& posts = Post.limit(3)& posts.flat_map do |post|& & ments.to_a& endend当一个请求调用这个方法的时候,在日志文件中我们会看到:一个查询得到了三个post对象,为了得到每个对象的评论又进行了更多的查询。Rails中,ActiveRecord的eagerloading特性可以显著减少查询次数,它可以让相关联的对象提前加载,这是通过调用内建Arel (ActiveRecord::Relation) 对象的includes(或preload)方法实现的。通过includes,ActiveRecord确保所有制定的关联都被加载,尽可能使用最小的查询次数:def comments_for_top_three_posts& posts = Post.includes(:comments).limit(3)& posts.flat_map do |post|& & ments.to_a& endend当以上代码被执行时,在log文件中我们看到,所有的评论都在单独的一个查询中被找到:N+1问题只是低效率一个例子,如果你没有引起足够的重视,类似的问题在你的应用中可能还会存在,问题在于你应该检查开发和测试日志文件来定位低效的代码。浏览日志文件是在应用进入生产环境之前找到低效率代码和改正它们的一个非常好的手段。否则,只要程序还在运行,你不会意识到性能问题,因为在开发和测试 环境下,数据量比生产环境要小的多。如果你维护一个新的app,在生产环境的数据集很小,它一样会工作良好,然而,随着数据量的增大,你的应用会变得越来越慢。常见错误7: 缺乏自动化测试Rails默认提供强大的自动测试功能。许多Rails开发人员使用TDD和BDD风格编写非常复杂的测试,并且善于利用强大的测试框架(如rspec和cucumber)。虽然在应用中添加自动化测试是如此的容易,但是我失望的是,在我接手的很多项目中,前任开发团队并没有(或很少)写测试用例。虽然很多人讨论应该如何进行测试,但很显然的是,至少应该有一些自动化测试存在每一个应用程序中。根据一般的经验,在控制器的每一个action中,至少有一个高级的集成测试。在未来的某个时候,开发人员有可能想扩展或修改代码,或者升级rails版本,一个良好的测试框架是验证应用基本功能正常的最好方式。这种方法的另一个好处是:它提供给未来的开发人员一个清晰的,描述功能完整的应用程序。常见错误8: 阻塞外部服务调用Rails服务的第三方提供者通常很容易通过gems集成它们的服务到你的应用中,但是如果你的外部服务中断或运行非常慢,应该怎么办呢?为了避免被这些调用阻塞,与其在正常的请求进程中直接调用这些服务,不如把它们移到后台工作队列中,一些流行的gems包可以解决这个问题:Delayed job & Resque & Sidekiq如果将不切实际或不可行的委托进程放在一个后台作业队列,对那些不可避免的情况或当外部服务出现问题的时候,你需要确保应用程序有足够的错误处理和故障转移机制。你也应该测试应用在没有外部服务的情况下,不会出现意料之外的后果。常见错误9: 不敢改动现有的数据库迁移Rails的database migration mechanism允许你创建指令去自动添加和删除数据库表和行,这是管理应用数据库模式变化的一个非常好的方式。虽然这适用于项目的开始阶段,随着时间的推移,数据库创建过程可能会花费较长的时间,有时还会出现迁移错位,插入顺序错误等情况。Rails创建一个当前数据库模式的映射文件,被称作db/schema.rb,当数据库migration运行的时候,它就会被更新。当没有migration的时候,schema.rb文件可以通过运行rake db:schema:dump生成。一个常见的错误是把一个新的migration放进源代码库中,但是却没有更新对应的schema.rb文件。当迁移已经失控,运行时间过长,或不再正常创建数据库,开发者不应该害怕清除旧的迁移目录,拷贝一个新的模式,并从那里继续。建立一个新的开发环境将需要一个rake db:schema:load而不是rake db:migrate,这是大多数开发人员所依赖的。Rials Guide中也有这些讨论。常见错误10: 将敏感信息放入源代码库中Rails框架可以轻松创建安全的应用程序,某些是通过使用一个secret token与浏览器安全会话。尽管现在这个token存储在config/secrets.yml, 文件从生产服务器的环境变量读取token,但以前的rails版本在config/initializers/secret_token.rb包含token。这个文件经常被错误的加进源码库,当这种情况发生的时候,任何可以访问代码库的人都可以轻松的危害你应用中的所有用户。你应该确保你的存储库配置文件排除了带有token的文件。你的生产服务器可以从环境变量中获取它们的token,或者通过dotenv gem。小结Rails是一个强大的框架,在构建强健的web应用过程中,它隐藏了很多丑陋的细节。然而,随着web应用开发越来越快,开发人员应该注意潜在的设计和编码缺陷,在应用规模逐渐增长的情况下,代码依然有良好的扩展性和可维护性。开发人员还需要注意那些使他们的应用程序变得慢,不可靠,更不安全的问题,在整个开发过程,研究框架并确保充分理解架构设计和编码,以确保生产高质量和高性能的应用程序是非常重要的。
看过本文的人还看过
人气:47340 更新:
人气:37084 更新:
人气:34560 更新:
人气:22610 更新:
ruby程序员的更多文章
大家在看 ^+^
推荐阅读 ^o^
我适合做你的妻子吗?(男女是否适合做夫妻的10个标准!)
婚姻这道题,你解好了吗?
又一项超级工程!中国人用七年建一座桥,创下这么多世界之最
伊朗有个姑娘为重病的男友做了一系列独一无二的点心..大家感受一下
猜你喜欢 ^_^
24小时热门文章
微信扫一扫
分享到朋友圈}

我要回帖

更多关于 websocket4net 例子 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信