当前位置: 首页 > news >正文

黄页软件推荐推广优化公司网站

黄页软件推荐,推广优化公司网站,网站中的公司地址怎么做,个人网站建设视频教学我不再装模作样地拥有很多朋友#xff0c;而是回到了孤单之中#xff0c;以真正的我开始了独自的生活。有时我也会因为寂寞而难以忍受空虚的折磨#xff0c;但我宁愿以这样的方式来维护自己的自尊#xff0c;也不愿以耻辱为代价去换取那种表面的朋友。 文章目录 一、项目设…我不再装模作样地拥有很多朋友而是回到了孤单之中以真正的我开始了独自的生活。有时我也会因为寂寞而难以忍受空虚的折磨但我宁愿以这样的方式来维护自己的自尊也不愿以耻辱为代价去换取那种表面的朋友。 文章目录 一、项目设计1. 游戏房间管理模块1.1 游戏房间的设计1.2 room类的实现1.3 游戏房间管理的设计1.4 room_manager类的实现 2. 匹配队列管理模块2.1 匹配队列的设计2.2 match_queue类的实现2.3 匹配队列管理的设计2.4 match_manager类的实现 3. 整合封装服务器模块3.1 业务请求分析通信接口设计3.2 服务器整体框架的实现 前端页面业务请求的框架实现3.3 http_callback3.4 wsopen_callback3.5 wsclose_callback3.6 wsmessage_callback 一、项目设计 1. 游戏房间管理模块 1.1 游戏房间的设计 1. 设计游戏房间的主要目的就是为匹配成功的两个用户实现一个小范围的关联关系即一个游戏房间内有两个下棋的玩家任意一个玩家的任何请求操作都会被广播给房间中的所有用户在游戏房间里面的请求其实只有两种一个是下棋请求一个是聊天请求而在游戏大厅中的请求其实也有两个一个是开始对战匹配请求一个是停止对战匹配请求。 有人可能会有疑问为什么游戏房价中任意一个用户的请求操作都会被广播给房间中的所有用户呢其实这个很好理解比如你在自己的前端页面上下了一步棋其实你不是直接下的而是先将下棋请求发送给服务器由服务器来检测你下棋的请求是否成功如果成功服务器才会返回响应但服务器返回响应不是返回给你一个人的服务器会把响应也返回给你的对手为什么要这么做呢道理很简单你在自己的前端页面上描绘了一颗棋子出现你对手的棋盘的对应位置需不需要描绘一颗棋子出来呢当然是需要的啊要不然你下你的我下我的随便下不就好了你下的棋子你对手看不着你对手下的棋子你看不着那不就乱套了么所以下棋这样的请求的响应服务器是要广播给一个房间中的所有用户的 同样对于聊天请求也是如此你发送了一段消息这段消息会被发送给服务器进行敏感词检测如果成功则服务器会把这段消息返回给房间中的所有用户对于不同的用户消息展示的位置是不一样的对于你来说消息应该展示在右侧而站在你对手的角度来说消息应该展示在他的左侧所以聊天这样的消息也是要转发给房间中的所有用户的因为这消息是需要双方都看到的道理和下棋一样房间中的两个玩家都要看到这些请求的响应。 2. 在了解上面服务器广播消息的原因之后我们来看一下一个游戏房间到底需要哪些成员变量属性才可以正好被描绘为一个房间。 每个房间肯定都需要有自己的唯一标识符那就是rid其次房间需要有两种状态一种是游戏开始的状态一种是游戏结束的状态为什么要有两种状态呢因为在不同的房间状态下用户退出房间的业务处理逻辑是不一致的如果游戏是开始的那么某一方用户退出另一方用户就是不战而胜因为有用户掉线了啊。如果游戏是结束的装填那么任意一个用户退出这都是正常的行为等到房间中的两个用户都退出之后此时房间正常销毁就可以了。所以还需要state房间状态player_number房间中的玩家数量。在房间中还需两个信息那就是为两个玩家分配白棋还是黑棋的用户id我们规定游戏进入游戏房间的用户为白棋选手我们为白棋选手分配white_id后进入游戏房间的用户为黑棋选手为黑棋选手分配black_id。 除了上面的几个信息外剩下的就是一些句柄了当游戏房间中胜负已分时我们要更新数据库中两个用户的信息所以还需要数据管理模块的句柄在用户发起聊天或下棋请求时我们要判断用户此时是否在线同时还要将请求处理后的响应广播给房间中的所有用户那么就还需要两个用户的websocket连接这两个需求的实现离不开在线用户管理模块句柄因为在线用户管理模块提供的两个最主要的功能就是判断用户是否在线 和 获取用户对应的websocket连接。同时也需要一个二维数组board来表示棋盘信息。 最后还需要一把锁来保护共享资源这里给大家下个定义所有可能被多线程同时访问的资源都叫做共享资源不考虑访问方式访问方式可能是修改也有可能只是拿一下值。 什么情况下共享资源需要被加锁保护呢当某一个资源既有可能被修改又有可能被访问拿值那么在多线程环境下就有可能产生安全问题此时我们要对这样的资源进行加锁保护如果某一个资源只会被多线程访问不会被修改那么这样的资源虽然是共享资源但其实我们不需要进行加锁保护因为他不会产生安全问题 所以只要可能产生线程安全问题那我们就进行加锁保护 3. 需要实现的函数有下面这么一串最主要的接口就是从下向上倒数的五个接口我们将房间中请求业务的处理都放在这里了例如handle_request就是处理请求的主接口根据请求req中请求类型的不同区分出该请求是下棋请求还是聊天请求如果是下棋请求那就在handle_request内部调用handle_chess接口并返回一个json格式的响应字符串如果是聊天请求那就在内部调用handle_chat接口也返回一个json格式的响应字符串值得注意的是退出房间的操作不是处理请求到来的接口他无需返回json响应只需要将玩家从房间中移除即可所以他是独立出来的一个业务处理接口封装成handle_exit。因为需要将业务处理后的响应广播给房间中的所有用户所以我们在实现一个broadcast接口用于将外部传入的json响应广播给房间中的所有用户。 其他剩余接口都是一些获取room类中成员变量 或者 设置room类中成员变量的辅助接口例如向房间中添加白棋和黑棋用户接口从房间中获取白棋和黑棋用户接口获取房间中玩家数量房间状态房间id等接口。 还有额外的两个私有接口check_win和check这两个接口是用来判断当用户下完这一步棋之后胜负是否已分有没有达到五子连珠。 1.2 room类的实现 1. 这些辅助接口的实现我就不说了大家看一眼就明白了对于white_idblack_id和player_number在多线程访问的时候可能会出现安全问题所以在修改的时候需要加锁保护。 值得注意的是线程安全是一种风险风险意味着可能会出错也有可能不会出错。如果不加锁保护一般在测试数据量较小的情况下可能不会体现出这种安全问题但只要数据量上来之后这种安全问题就会立马体现出来所以只要涉及到可能产生线程安全问题的成员变量对这种变量的操作我们就都需要进行加锁保护 虽然一个项目中可能处处要进行加锁这会导致服务器的效率会降低一些但服务器稳定才是最重要的我们的底线是服务器不能挂掉同时服务器不能出错所以加锁保护是一件必要的事情没得商量 2. 在判断胜负这里其实就是从当前的row和col的下棋位置分别从竖着的这条线横着的线正斜的线反斜的线四个方向来判断是否有五个颜色相同的棋子我们是以加减偏移量的方式来判断各个方向上是否有五星连珠比如横纵偏移量是01那么加上这个偏移量之后棋子的位置就会便宜到当前位置的右边我们就一直向右判断直到遇到棋盘边界或者遇到颜色不相同的棋子后就停下来这样就完成了一条线上以当前位置为基准一侧的判断接下来就是让cur_row和cur_col回到原始的位置让横纵坐标分别减去这个偏移量那么此时棋子的位置就会便宜到当前位置的左边然后再一直向左判断停止的条件和向右判断一样这样就完成了横线上是否有五子连珠的判断。其余的三根线判断方式也是如此最后只要有一根线上满足五星连珠的条件那么我们就说此刻棋局结束某一方赢得了胜利。 需要特殊说明一点的是_board和ret这两个变量都是可能产生安全问题的共享资源所以在访问他们或者修改他们的时候一定要加锁控制下面代码中我也是使用了RAII风格的加锁方式来进行保护。 3. 在处理请求字段这里我们需要先了解一下前后端报文格式的设计因为只有知道了前后端通信的报文格式的协议之后我们才能解析请求报文从而判断请求类型是什么进而做出相应的业务处理这样的协议一定要在项目实现前双方都确定好因为这样的通信报文协议一换前后端交互的报文格式都需要更改那代码的改动量就会变得非常大所以一定要提前定制好通信时报文的格式是什么。 游戏房间中的请求都是websocket请求所以我们直接采用json序列化和反序列化的方式来进行数据的通信。 下面是下棋请求和下棋请求成功时/失败时的json响应格式。 下面是聊天请求和聊天请求失败时/成功时的json响应格式。 4. 在处理请求时首先判断一下请求中的房间号是否与本房间相同如果不相同那就直接构建一个json响应消息原因就是房间号不匹配并且把这个消息广播给房间中的所有用户这算是一种提前校验的方式主要用来帮助我们进行将来可能产生的不同种类情况的请求进行处理大部分情况下前端那里发送的websocket消息是会发送到对应的房间中的。 校验房间号没问题之后就通过req中的optype来区分是下棋请求还是聊天请求如果是下棋请求则直接调用handle_chess函数将req传过去同时返回一个json格式的resp如果resp里面的winner不为0那么就说明在用户下完这步棋之后胜负已分那我们就需要更新数据库中用户的信息更新的过程其实就是调用user_table类里面的win和lose怎么调用呢很简单通过user_table类的句柄就可以调用。 此时已分胜负之后房间的状态信息我们还要更改一下值得注意的是_state也是可能产生线程安全问题的共享资源所以我们也要进行加锁保护。 对于聊天请求的处理就比较简单了不需要更新房间状态信息以及数据库的信息直接调用handle_chat进行聊天信息的检测就可以了。 如果optype的类型既不是put_chess又不是chat那么此时我们就返回一个错误信息表示当前请求的类型是未知的。 最后将业务处理后的resp响应广播给房间中的所有用户即可。 5. 在下棋请求的业务处理这里可能一个用户刚下完棋然后对方就退出游戏房间关闭前端页面了那么此时对应的websocket连接就断开了这个时候游戏房间在线用户管理中就会移除这个用户所以第一步我们先判断白棋和黑棋用户哪个不在游戏房间在线用户管理中只要有一个不在那就说明有一个用户掉线了另一个获得胜利返回resp响应信息即可。 如果两个用户都在线那么就判断当前下棋请求中的下棋位置是否已经有棋子如果有那么就返回一个错误信息您所下的位置已有棋子如果下棋位置没有其他棋子那就下棋成功更改board上面对应位置的值由于board需要被保护所以我们进行加锁控制。 下棋成功之后接下来就是判断下棋之后是否胜负已分如果胜负已分那么就将resp中的winner设置为胜利用户的uid如果胜负未分那么就将resp中的winner设置为0。 6. 处理聊天请求是比较简单的其实我们就是进行一个敏感词的检测比如不允许发送侮辱人的词汇等等这里只是拿垃圾做一个样例如果后期想要扩展这里可以在封装一个专门添加敏感词的接口。不过我们这里就不做了。 如果消息中不包含敏感词那就直接返回resp即可在handle_request中会统一进行resp的广播。 7. 在处理退出房间的业务时如果此时房间状态是GAME_START这个时候如果有玩家退出了房间那么另一个玩家就是不战而胜现在已经分出了胜负那当然就得进行数据库信息的更新了同时别忘记将房间的状态信息更改为GAME_OVER因为胜利的玩家在退出房间时此时也会调用这个接口这个时候不应该走到if的分支语句里面去而应该是将房间中玩家数量- -然后正常返回。 由于state的判断与更改player_number的更改都是不安全的所以我直接在if外面进行了加锁保护。 8. 广播消息实现起来也不难将参数resp进行序列化然后通过在线用户管理模块的句柄来获得白棋和黑棋玩家的id通过id来拿到websocket通信的句柄也就是conn智能指针通过调用connection类里面的send函数就可以在websocket连接上发送已经序列化好的body数据。 1.3 游戏房间管理的设计 1. 每个房间都有自己的房间id所以成员变量需要有一个房间id分配器。由于房间的构造函数需要我们传入user_table和online_manager的两个句柄所以房间管理这里也需要这两个成员变量。 由于房间可能会存在多个所以我们需要先描述再组织描述的过程我们上面已经完成了组织的过程我们通过哈希表来进行组织构建房间id和房间对象之间的映射关系当然哈希表不能直接存储房间对象要不然需要的空间太大了那我们就存储管理房间对象的智能指针这个智能指针也必须是shared_ptr原理我前面应该说过主要是因为在向哈希表中插入键值对的时候会发生智能指针的拷贝所以我们只能使用shared_ptr。 最后封装服务器模块时服务器模块一般都只知道uid是多少所以经常调用的接口是通过uid来获取房间信息所以我们还得构建uid和房间信息的映射关系但这样其实是没必要的因为我们已经有了_rooms这个哈希表了想通过uid找到room_ptr其实只要衔接上uid和rid的关系就可以所以我们只需要再搞一个存储uid和rid的哈希表就可以了这样也能节省一些空间。 最后成员变量肯定离不开互斥锁因为这里边有三个共享资源两个哈希表一个id分配器。对于ut和om句柄的操作我们是不需要保护的因为这两个模块中所有的接口在当时实现的时候就实现为线程安全的了所以不需要被保护。 2. 当两个玩家在游戏大厅中匹配成功之后我们就应该为这两个玩家创建一个游戏房间所以必须提供一个create_room接口。通过rid和uid来获取房间详细信息的接口也应该提供出去以及通过rid来销毁房间通过uid来删除房间中的指定用户等接口我们都public提供给外部。 1.4 room_manager类的实现 1. 在创建游戏房间时需要外部传入对战匹配成功的两个用户的uid创建游戏房间的前提是两个用户都必须在游戏大厅在线管理中只要有一个不在那就说明有一个用户关闭游戏大厅的页面了或者是停止匹配了此时我们是不为这两个用户创建游戏房间的。 当两个玩家都在游戏大厅时此时就创建出一个游戏房间同时向这个游戏房间里面添加白棋用户和黑棋用户其实这里吧还是可以修改的我们可以直接在room的构造函数里面多加两个参数分别代表黑棋和白棋用户这样在创建room对象的时候就可以直接在构造函数里面传参而不需要创建房间之后通过调用add_black_user和add_white_user来进行房间中用户的添加了这里我也不带要改动了就这么滴吧改动和不改动对服务器的效率也没啥大的影响这里就这么搞了等后面大家拿到项目源码的时候如果想改那就自己改改吧。 创建好房间之后剩下的操作就是向哈希表中插入键值对通过哈希表来进行房间的管理然后在给房间id分配器自增1最后返回游戏房间的句柄即可。 2. 通过rid来查找room_ptr和通过uid来查找room_ptr实现起来确实都不难无非就是在哈希表根据key值来进行键值对的查找找到之后返回迭代器指向的second即可如果没有找到则返回一个空的room_ptr对象即可。 由于上面的操作都是对哈希表进行访问所以访问的过程都要加锁保护。 3. 在destroy_room里面首先进行房间信息的判断如果房间信息为空那么就说明没有房间那就直接返回即可。如果不为空那就需要进行房间的销毁工作但怎么销毁房间呢其实很简单只要从哈希表中移除包含房间room_ptr的键值对就可以销毁房间了因为整个类里面只有哈希表会一直在堆上存储着管理房间对象的智能指针一旦智能指针被销毁那么房间对象所占用的内存也就会被释放。 不光要销毁房间房间id和用户id的关联键值对也要进行移除这些步骤都需要进行加锁保护。 在remove_room_user里面我们需要对特定的某个房间中的某个用户进行移除那就先通过get_room_by_uid来获取到房间的详细信息如果房间为空则什么也不做直接返回。如果不为空则调用room类里面的handle_exit接口进行房间中用户的移除。在移除过后我们需要判断房间中玩家的数量如果房间中没有玩家了那么就需要调用destroy_room进行房间的销毁。 2. 匹配队列管理模块 2.1 匹配队列的设计 1. 其实所谓的匹配队列就是阻塞队列我们匹配队列总共会实现三个分别对应不同档次天体分数的玩家进行匹配青铜玩家只匹配青铜选手白银匹配白银的黄金匹配黄金的所以我们一共会实现三个阻塞队列分别代表三种不同档次玩家的匹配过程。 这三个队列其实就是blockqueue除此之外我们还需要为每个阻塞队列创建出一个匹配的线程这个线程其实就是消费者用于判断当前队列中用户的个数是否超过2个如果超过2个则出队头的前两个用户为这两个用户创建游戏房间以上就是匹配队列的设计思想。 2. 在实现匹配队列的时候我们选用的底层容器不是STL提供的queue而是选用双向链表list。 可能存在这么一种情况大量的青铜选手向服务器发起了对战匹配的请求那么服务器的青铜阻塞队列就会被塞满大量的用户当然这也是可能发生的啊因为这个阻塞队列的消费线程会在队列中元素大于2的时候取出前两个用户所以你可以想象成阻塞队列里面是有可能堆满很多的等待匹配成功的用户的因为一个消费线程进行消费和websocketpp库里面的多线程/我们自己的主线程向阻塞队列立马push用户这两个操作都是互相阻塞的因为只要涉及到对阻塞队列进行访问就会进行加锁保护所以到底阻塞队列里面会出现多少个用户这是不确定的有可能100个用户发起对战匹配请求有10个被消费线程消费了同时创建了5个游戏房间也有可能是11被消费线程消费了即将要创建第6个游戏房间这些都是不确定的因为多线程所造成的情况是非常复杂的我们是无法提前预知判断的我们能做的只能就是说尽可能的把所有情况都想一遍让我们的服务器能够解决我们尽力想到的所有业务场景下可能会产生的问题等实际项目上线时如果在产生了问题我们在进行不断的改进就好。 所以就会有阻塞队列中一串用户中间的某个用户想停止匹配那么此时阻塞队列就要有能够将这个用户移除的功能但移除中间的某个用户STL的queue是无法做到的所以我们就选用了list这个容器。我们实现的阻塞队列那是典型的生产消费模型也就是典型的线程同步与互斥的使用场景所以锁和条件变量肯定是没的跑啦匹配队列这里一共也就包含了这三个成员变量。 3. 在匹配队列这里我们需要向外部提供的成员函数有push用于向阻塞队列中添加用户pop用于出队头的用户同时将用户的uid以输出型参数的方式来让外部获取remove就是我们最开始提到的如果队列中间的某个用户突然不想匹配了那队列需要提供能够从队列中移除特定用户的接口size用于获取队列中元素的个数empty用于判断队列是否为空wait也是比较重要的接口用来阻塞线程当队列中元素个数不到2时消费线程应该在该队列类里面所创建出来的条件变量_cond下阻塞等待直到队列中元素个数超过2之后此时唤醒条件变量下阻塞等待的线程让消费线程进行取队头用户创建游戏房间的操作。 2.2 match_queue类的实现 1. 在push这里我们实现的思想是只要入队一次数据那我们就唤醒一次线程让线程去看看是否现在能出队用户进行游戏房间的创建了所以我们将push_back和notify_all两个接口一起放在push里边实现了。对list的操作会涉及到线程安全问题所以我们进行加锁保护另外唤醒这个接口也需要在加锁的条件下进行操作也就是在临界区里面进行操作。 这是为什么呢因为notify的操作其实就是释放锁把锁释放将锁归还给_cond.wait()的线程哪个线程执行了_cond.wait()而导致在条件变量下等待那么notify就会把锁归还给哪个线程你既然都归还锁了那么前提就是你得有锁啊那你在哪能有锁呢只能在临界区的时候才有锁所以调用push接口的线程(这个线程可能是我们自己服务器的主线程也有可能是websocketpp库里面的多线程)就会把锁归还给条件变量下等待的消费线程此时消费线程就可以执行他自己的逻辑了。 多说一嘴这里使用的是notify_all而不是notify_one其实这两个都行因为我们的项目是一个线程对应一个阻塞队列一个阻塞队列对应一个条件变量三者是1 : 1 : 1的所以你唤醒一个还是唤醒全部到头来都是一样的因为一个阻塞队列只会匹配一个消费线程 我们这里的pop只是pop一次队头的元素而不是pop两次因为你无法保证队列里面此刻就有两个用户我们以输出型参数的方式来将用户的uid进行返回将其交给外部整体的过程都要进行加锁保护因为我们一直在访问list。 移除指定的用户我们就不自己操心了直接调用STL里面list实现的接口remove即可将指定用户从队列中移除。同样的依旧需要进行加锁保护。 2. 获取队列元素个数队列判空这些接口STL早就帮我们实现好了我们只需要进行加锁控制就可以。然后我们在实现一个阻塞消费线程的接口_cond.wait接口必须传递unique_lock的RAII的锁不能传递lock_guard和原生的mutex锁。 主要是因为线程在条件变量下等待的时候是抱着锁等待的也就是在临界区进行等待而在实际等待期间线程又会释放锁等线程被唤醒的时候由于线程是在临界区被唤醒的所以线程被唤醒之后又要拿着锁那么这个过程就涉及到任意申请锁和释放锁的操作所以lock_guard是肯定不行的因为他是纯RAII风格的锁只有在对象被销毁的时候才会主动释放锁无法手动式的任意申请锁和释放锁。 那为什么不选用原生的mutex锁而选择使用unique_lock呢主要还是因为unique_lock是RAII的使用起来要比原生的mutex更为灵活和安全 C11线程库 2.3 匹配队列管理的设计 1. 在匹配队列管理这里我们要创建三个不同档次的匹配队列对用户进行划分等级的对战匹配同时还需要创建三个分别匹配不同档次阻塞队列的消费线程。 创建房间肯定得需要room_manager的管理句柄将用户添加到不同档次的匹配队列里面那就需要拿到该用户的score分数那就还需要user_table的管理句柄在两个用户对战匹配成功后我们需要给客户端也就是前端浏览器返回一个json响应告知双方用户对战匹配成功所以还需要一个在线用户管理模块句柄也就是online_manager类的对象指针_mm 2. 由于我们创建出来了三个线程每个线程肯定要有对应的线程执行入口函数所以在私有方法里面分别实现了三个线程的入口函数由于每个线程执行的逻辑都是一样的所以又实现了一个统一的私有方法handle_match。 公有函数其实就两个接口当服务器收到客户端的对战匹配请求后需要将用户添加到指定的匹配队列里面所以我们提供一个add接口当服务器收到客户端的停止对战匹配请求后需要将用户从特定的匹配队列中移除所以我们提供了一个del接口。 2.4 match_manager类的实现 1. 我们知道线程被创建出来之后一定是要被join的否则就会造成线程的PCB资源泄露但这里不太一样一旦消费线程跑起来那就是死循环需要一直检测是否队列中有两个用户如果有则pop创建游戏房间如果没有则需要一直死循环在条件变量下进行阻塞等待所以这里无需调用thread.join接口。 在实现时需要判断出队头的操作是否成功对于队列中的第一个元素来说如果取出的操作失败那就什么都不用做直接continue重新进行循环的逻辑执行如果第一个取出成功第二个取出失败那就需要将第一个元素重新放回到队列里面然后再continue重新进行循环的逻辑执行。当两个用户都被取出来之后下一步就是判断两个用户是否都在游戏大厅中只要有一个不在游戏大厅中那就说明此时有某个人掉线了可能断网了或者他自己关闭游戏大厅的页面了那么此时我们要把相对的那个人重新放回匹配队列中然后continue重新执行循环逻辑。 上面的检测成功之后那就为两个用户创建游戏房间如果游戏房间创建失败则将两个用户重新放回阻塞队列中continue重新执行循环逻辑。如果房间创建成功那我们就构建响应表示对战匹配成功通过conn1和conn2来将响应分别返回给客户端即可。 如果队列中人数不大于2那么线程在条件变量下阻塞等待即可。如果大于那就执行上面所说的逻辑。 2. 添加用户到指定匹配队列则需要先通过_ut句柄拿到玩家的天梯分数值根据天梯分数的不同调用不同的阻塞队列进行玩家的push 3. 移除玩家的逻辑也是同样如此需要先根据天梯分数确定出玩家所在的匹配队列然后调用我们之前实现好的remove接口进行指定玩家的移除 3. 整合封装服务器模块 3.1 业务请求分析通信接口设计 1. 在整合封装服务器模块这里需要做的事情有两件第一件是搭建好一个基本的http/websocket服务器出来能够完成服务端和客户端的通信第二件是针对客户端发起的一系列业务请求进行相应的业务处理而业务处理的完成其实就是通过我们前面所实现的一系列模块来进行的。 2.客户端发出的业务请求都有 1.注册页面register.html的获取请求 2.点击注册提交按钮发起注册请求请求通过后应跳转到登录页面 3.登录页面login.html的获取请求 4.点击登录提交按钮发起登录请求请求通过后应跳转到游戏大厅页面 5.游戏大厅页面game_hall.html的获取请求 6.用户个人详细信息的获取请求游戏大厅要展示用户的昵称总战斗场次胜利场次等详细信息 7.发起websocket握手的HTTP请求进入游戏大厅后连接要从http切换为websocket 8.在游戏大厅页面发起对战匹配请求请求通过后应跳转到游戏房间页面 9.在游戏大厅页面发起停止对战匹配请求 10.游戏房间页面game_room.html的获取请求 11.进入游戏房间后发起websocket握手的HTTP请求页面切换原来的websocket连接会断开 12.在游戏房间页面发起下棋请求 13.在游戏房间页面发起聊天请求 14.游戏结束点击返回大厅按钮游戏大厅页面game_hall.html的获取请求 3. 在上面的业务请求中前6个请求都是纯http请求135都是一种静态资源的请求也就是请求服务器上面的静态网页而246是动态功能的http请求是服务器要做相应的业务逻辑处理的而不是简单的返回一个html网页就好。从7开始向下的请求可以归类为websocket连接上的请求包括协议切换的http请求和websocket连接上发起的业务请求。 4. 在上面了解了业务请求的分类之后接下来还需要了解通信接口的设计例如请求字段中包含什么值代表这个请求是获取网页资源的请求又或是对战匹配的请求又或是动态登录功能的请求这些字段的含义需要提前客户端和服务器双方约定好这样在实现的时候两者都是遵守协议来进行请求报文或响应报文的发送的。这样才能够成功实现双方的正确业务交互。 本项目中服务器和客户端通信接口采用的是Restful风格Restful风格其实是依托于http协议来实现的也就是说前6个http请求的格式都是Restful风格的请求或响应正文采用json/xml的格式来进行组织请求头和响应头中GET表示获取资源POST表示新增资源PUT表示更新资源DELETE表示删除资源 —下面是通信接口的设计如果后面在构建服务器的响应和客户端的请求时忘记字段的值可以返回这里来看看看当时定制的协议是什么样子的。 对于服务器上静态资源的请求http请求行的请求方法都是GET后面的就是请求路径也就是web根目录具体的资源路径第三个字段就是http协议版本。 服务器收到http请求之后就会构建http响应将响应返回给客户端浏览器响应正文的内容就是根据http请求中解析出来的uri资源。 当浏览器发起用户注册的动态功能请求时http请求行的请求方法为POST意味着向服务器新增资源请求路径则命名为reg表示进行用户注册的功能请求请求正文里面则携带一个Json组织的字符串包括username和password这两个字段。 当服务器收到请求之后会进行后端的业务处理比如看看这个用户是否已经存在过了如果存在过则请求失败我们返回一个失败的响应响应正文也为json组织的字符串包括result和reason这两个字段如果数据库中没有这个新增用户的数据那就说明请求成功返回成功的响应就可以响应正文为json组织的字符串只需要包括resutl这一个字段就可以了。 登录时的请求行和注册时的请求行大致一样唯一不同的是urlurl为login表示登录动态功能的请求。请求正文与注册时的正文一致。 当请求成功时只需要返回result为true的一个json格式的字符串即可当请求失败时描述好失败的具体原因即可。 获取客户端信息的http的请求方法应该是GETurl为userinfo表示客户端此时要请求拿到用户的详细数据当服务器收到响应后如果该用户存在那么就从服务器中拿到用户的详细数据并构建成为一个json格式的字符串返回给客户端即可如果该用户不存在构建失败的响应返回即可。 当进入游戏大厅后获取完用户的详细信息后紧接着就要和客户端建立websocket长连接在前端这里发起长连接的请求其实只要new一个WebSocket对象就可以后面讲解服务器模块的时候大家就会知道怎么搞了至于服务器完成websocket的第二次握手这个过程我们自己不需要做websocketpp这个库会自动帮我们完成这个工作。 当websocket连接正式建立成功后服务器这边会自动调用wsopen_callback回调函数此时在这个回调函数里面我们给客户端返回一个json响应下面的图为了方便大家看所以展现的是没有序列化后的json格式数据在发送的时候我们只需要将其序列化一下即可这里重点是为了让大家对请求和响应的各个字段混个眼熟后面在组织响应和请求的时候这些字段前后端一定要匹配上如果不匹配则肯定会发生错误的比如解析报文后看不懂报文里面字段的值。 下面的所有请求和响应就全部都是websocket报文格式的了和http没有关系了所以我们直接构建json格式的字符串进行发送即可。 开始对战匹配请求的optype为match_start表示匹配开始后端这里需要进行两次的回复第一次是将用户添加到对应的匹配队列里面了那么此时给客户端返回一个您已成功加入匹配队列之后的响应如果没有成功加入匹配队列则还需要填充好reason字段表明未加入匹配队列的原因。 第二次响应是如果有其他玩家和当前这个玩家匹配成功了那服务器也要给客户端返回一个包含match_success的optype字段的json格式的响应字符串。 停止对战匹配的请求字段就是match_stop如果停止成功则返回true如果停止失败则返回false同时说明原因。 在进入游戏房间后首先要做的是重新建立websocket长连接只不过我们稍微更改一下uriuri为/room。 当游戏房间成功进入之后服务器要为客户端返回一个房间的详细信息例如白棋id黑棋id包括自身id因为房间中的两个玩家都会给服务器发起进入游戏房间的请求self_id就是服务器告诉各个浏览器客户端他们自己的uid是什么。 下棋请求的字段要包括下棋的行和列以及下棋用户的uid还有optype为put_chess等。 当下棋失败时要组织好响应表明下棋失败的具体原因是什么。 如果下棋成功则也要返回原因原因包括您胜利了您失败了胜负未分等除此之外还需要在请求的所有字段的基础上再加一个winner字段winner为0表示胜负未分winner为哪个uid则表明该用户获得了胜利。 聊天请求这里optype为chat需要包括message是什么哪个用户发起的聊天请求用户所在的房间信息是什么等字段信息。 3.2 服务器整体框架的实现 前端页面业务请求的框架实现 1. 在服务器代码模块这里我们要实现两个部分一个是搭建出服务器gobang_server类对象让gobang.cc文件中可以直接实例化出服务器对象然后调用一个run接口让服务器跑起来另一个部分也就是最繁琐的部分就是服务器四个回调函数接口的实现这四个回调函数中处理了来自客户端所有的业务请求从连接类型来看业务请求说白了就是http请求和websocket请求而这四个回调函数对应了http请求和websocket请求的处理函数所以业务处理其实就是实现这四个回调函数。 但由于业务太多所以我们要另外封装出许多的接口每个接口对应一个业务请求的处理。 static_handler负责处理客户端的静态资源请求register_handler负责处理注册功能请求login_handler负责处理登录功能请求info_handler负责处理获取客户端信息的请求http_callback是总的http请求的回调函数也就意味着所以客户端的http请求都会被发送到这个接口内部进行处理wsopen_game_hall和wsopen_game_room是在wsopen_callback内部进行调用的也就是websocket连接建立成功后的回调函数wsclose_game_hall和wsclose_game_room是在wsclose_callback内部进行调用的也就是websocket连接断开后的回调函数wsmessage_game_hall和wsmessage_game_room是在wsmessage_callback内部进行调用的也就是客户端请求的所有的websocket消息都会被发送到这个接口进行处理。 上面这些接口都是服务器内部私有的业务处理接口对外只公开服务器的构造函数和使得服务器跑起来的run接口。 2. 在register.html这里会发起两次http请求第一次是通过ajax发起注册功能请求当请求成功后还会通过location.replace发起第二次登录页面的获取请求 3. 当跳转到登录页面后在这个地方也会发起两次http请求第一次也是通过ajax发起登录功能的请求当登录成功后会通过window.location.assign发起第二次获取游戏大厅页面的http请求。 js我也没系统的学过我也只是能看懂这些业务逻辑请求的代码而已所以给大家细讲这些代码的原理我其实是讲不了的我是做后端的所以重心也不会放在前端这里只要能把前端这部分的业务逻辑理清楚目的就达到了。 4. 在游戏大厅这里主要是四个请求首先需要发起一次获取用户详细信息的HTTP请求在获取完用户详细信息并展示到大厅页面后再发起一次协议切换的HTTP请求与服务器建立websocket长连接建立好websocket长连接之后大厅中则只会有两种请求一种是开始对战匹配的请求一种是停止对战匹配的请求这两种请求都是需要通过点击按钮来完成的我们也是通过给按钮添加点击事件当触发按钮之后向服务器发送对应匹配的websocket请求。 当websocket消息发送给服务器后服务器会返回websocket响应消息响应类型也分四种分别是hall_ready代表游戏大厅已经准备好了match_start代表用户成功被加入到匹配队列里面match_stop代表用户已经从匹配队列中移除match_success代表用户对战匹配成功如果对战匹配成功也要发起一次http请求用于获取游戏房间的页面这个请求也是通过location.replace来发起的。 5. 当进入到游戏房间页面后第一件事就是协议切换请求因为原来的websocket长连接已经关闭了。 在建立好websocket长连接之后游戏房间中就只有两类请求了一种是下棋请求一种是聊天请求这两类请求都是以websocket消息发送给服务器的自然服务器也会返回响应消息前端需要根据返回的响应消息来进行棋子的描绘以及消息界面消息的展示。 3.3 http_callback 1. 下面是处理四种http请求业务的回调函数http_callback的总调用逻辑。 下面是封装了统一的http响应的函数如果对http进行响应的话则直接调用该接口即可。 下面是解析http中cookie字段的函数通过调用最早之前我们实现的string_util::split函数来实现的先截取出来包含Cookie的字段然后在该字段里面截取出来SSID的内容这个内容就是服务器需要的会话id服务器为客户端提供任何服务器之前都会进行会话验证判断该用户是否已经登录成功只有登录成功的用户服务器才会为其提供服务 服务器总不能一股脑为所有用户都提供服务吧这样绝对是不合理的 下面是获取用户信息的业务处理接口 下面是用户登录请求的业务逻辑处理 下面是用户注册的业务逻辑处理 下面是客户端获取服务器上静态web资源的业务处理 3.4 wsopen_callback 下面是通过cookie来拿到会话详细信息的封装接口后面服务器提供服务时会常用会话信息中的用户id来进行业务逻辑处理所以这里做了统一的封装。 下面是游戏大厅长连接建立成功后的业务处理函数 下面是游戏房间长连接建立成功后的回调函数 下面是总的websocket连接建立成功的回调函数 3.5 wsclose_callback 下面是游戏房间和游戏大厅页面关闭时的逻辑处理 3.6 wsmessage_callback 下面是游戏大厅和游戏房间中websocket消息请求的业务逻辑处理
http://www.huolong8.cn/news/338941/

相关文章:

  • 自己的网站可以做淘客吗网站开发部职责
  • 网站开发前端后端书籍网络营销代运营外包公司
  • 制作网页的网站fa兰州市建设厅官方网站
  • 泉州网站建设有哪些erp123登录入口
  • 网站制作公司去哪找wordpress怎么能把文章采集
  • 网站服务器下行很多是什么意思青浦建设网站公司
  • 优化网站的步骤静态网站可以做留言板
  • 国外主流网站开发技术 天堂在线搜索
  • 官方网站查询 优帮云简单网页制作html
  • 网站制作公司北京网站建设都需要提供什么资料
  • 城市建设理论研究上传哪个网站网页图片抓取
  • 金昌市网站建设电商培训机构排名前十
  • 室内设计师个人网站深圳网站建设制作优化
  • 烟台建网站哪家好自己做盗版小说网站吗
  • 网站错位短视频素材下载网站 免费
  • php小型网站开发服装网站开发的需求分析
  • 电商网站建设用php做pc端网站平台
  • 深圳网站网络推广公司杭州网站建设V芯ee8888e
  • win7如何建设免费网站新兴县城乡建设局网站登录
  • 上海 网站开发企业在公司做的网站遇到的问题
  • 吐鲁番做网站竞价排名采用什么计费方式
  • 汕头智能模板建站世界服装鞋帽网免费做网站
  • 新闻类网站开发特点中国万网陈峰欣
  • 好的营销网站设计公司服务器维护中什么意思
  • 网站备案信息不准确安丘网站建设报价
  • 自己怎么做公司网站这几年做啥网站致富
  • wordpress+配置七牛seo顾问收费
  • 网站在线订单系统怎么做又一个wordpress网站
  • 网站建设应列入啥费用免费企业网站哪个好
  • 肇庆住房和城乡建设局网站南京宜电的网站谁做的