内容简介:C/S构架中,客户端与服务端一般通过TCP通信。建立连接后即验证身份验证,若账户密码正确,TCP连接保持,然后client和server全双工通信。在B/S构架下,若希望用户通过浏览器也能实现客户端相同的功能,我们可以开发一个中间层为webserver,用户浏览器与webserver交互,webserver再通过tcp连接与真正的server交互。首先需要明确client与server通信格式。包括如何登陆,如何实现对资源的CURD。通过Wireshark可以抓取明文消息,对于加密消息,需要查阅源代码了解加
C/S构架中,客户端与服务端一般通过TCP通信。建立连接后即验证身份验证,若账户密码正确,TCP连接保持,然后client和server全双工通信。
在B/S构架下,若希望用户通过浏览器也能实现客户端相同的功能,我们可以开发一个中间层为webserver,用户浏览器与webserver交互,webserver再通过tcp连接与真正的server交互。
首先需要明确client与server通信格式。包括如何登陆,如何实现对资源的CURD。通过Wireshark可以抓取明文消息,对于加密消息,需要查阅源代码了解加密方式。
消息体格式
业务消息采用明文,敏感登录信息采用非对称加密RSA结合对称加密DES。
TCP连接登陆需要四个字段,分别为
用户名
密码
desKey
desIV
针对以上字段内容,使用EncryptPKCS1v15(C#系统默认加密方式)加密,再使用base64编码,构建xml包。在包头使用binary.Write写入msgLength和msgType,完成封装发送给远端服务器。对于返回值,使用刚发送的desKey结合desIV进行DES解密,获得登陆结果。
功能流程
浏览器->web service->TCPServer
image.png
在以上通信流程中,浏览器端使用人数较多,频繁建立连接。web service和TCPServer保持一个长连接,多用户共享此TCP长连接。
对于webservice与TCP server通信,利用Routine结合Channel方式协同工作。实现TCP全双工的关键Routine如下:
SendEventProcessor
监听waiting process queue,若有则构建pakage,然后通过tcp发送到服务端,然后将此Event转移的pending response队列,考虑到允许删除无响应event,pending response队列采用slice结构。
// work as runtine // func SendEventProcessor() { var task *Event for { Event= <-EventWaiting sendMessage(Event.action, Event.data) if Event.action != "HeartBeat" { log.Printf("Event message was sent...%s", Event.action) EventPendingMutex.Lock() EventPending = append(EventPending,Event) EventPendingMutex.Unlock() } } }
FrameDetector:由于TCP read buffer可能存在粘包、拆包、废弃包。此routine用于实时过滤tcp read buffer,提取出完整有效的数据包。
func FrameDetector() { buf := new(bytes.Buffer) //滑动窗 buf4bytes := make([]byte, 4) //tmp var cRdr := bufio.NewReader(conn) //reader from connection frame := Frame{} for { b, err := cRdr.ReadByte() if err == io.EOF { log.Println("readFrame:connection closed,connecting...") RemoteConnect(targetServer) } buf.WriteByte(b) if buf.Len() == 8 { buf.Read(buf4bytes) frame.Length = int(binary.LittleEndian.Uint32(buf4bytes)) //little endian 低字节先发 buf.Read(buf4bytes) frame.Type = int(binary.LittleEndian.Uint32(buf4bytes)) //little endian 低字节先发 switch frame.Type { case 3://msgType, login response ... /create frame from bytes FrameChan <- &frame buf.Reset()//clear bytes buffer after saving the frame break case 4, 5, 411: // response frame.Message = make([]byte, frame.Length-4) n, _ := cRdr.Read(frame.Message) if n < frame.Length-4 { //continue waiting and reading for n != frame.Length-4 { b, _ := cRdr.ReadByte() frame.Message = append(frame.Message, b) n++ } } FrameChan <- &frame buf.Reset() break default: buf.ReadByte() } } } }
RecieveMessageProcessor:
读服务端返回的有效Frame,解析其中数据为,从pending response队列寻找目标Event,把返回结果写进去,同时标记done,从pending中移除。
// should be work as runtine func RecieveMessageProcessor() { var frame *Frame for { frame = <-FrameChan log.Printf("receiveMessage:length:%d,type:%d", frame.Length, frame.Type) switch frame.Type { case 3: // 提醒事件 //parse xml string and save as a map m := parseXML(frame.Message) for i, event := range EventPending { //查找到工作中的任务,标记为完成 if event.action == "Login" { event.response = m event.done <- true //向监听runtine发送完成信号 EventPendingMutex.Lock() EventPending = append(EventPending[0:i], EventPending[i+1:]...) // 将任务从工作表中清除 EventPendingMutex.Unlock() break } } break } }
完成tcp连接后,发起登录事件,阻塞等待登陆反馈。同时为避免TCP连接掉线问题,新开routine,每隔数秒发送HeartBeat。
故初始化流程如下
image.png
调用接口
为便于HTTP调用,编写CRUD接口函数,函数中构建Event事件、构建NewTimer定时事件,使用select channel方式判断超时。若超时,则从queue中删除此event,同时返回超时信息给http handler。
func Delete(id,name, formula string) map[string]string { data := map[string]string{"ID": id, "name": name, "content": formula} event := Event{action: "Delete", data: data, done: make(chan bool, 1)} EventWaiting <- &event timer := time.NewTimer(timeout) defer timer.Stop() select { case <-timer.C: dropTask(&event) event.response = map[string]string{"status": "error", "message": "time out"} case <-task.done: //do something break; } return task.response }
注意点:
time.After()在触发前,即便父函数返回也不会被garbage collector回收。仅触发后或stop状态的timer,会被gc回收。
使用正则从xml字符串提取有用信息,需要先剔除字符串中特殊字符(ascii<32)
关于TCP/ip报文,以及各控制字功能,握手流程、挥手流程,CLOSE_WAIT和TIME_WAIT参考此文 https://www.cnblogs.com/myd620/p/6252135.html
关于RSA,有多种加密模式,C#默认的是EncryptPKCS1v15
关于DES padding模式,C#默认的是PKCS7Padding
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- (译)通过WebChannel/WebSockets与QML中的HTML交互
- iOS 12 人机交互指南:交互(User Interaction)
- 生活NLP云服务“玩秘”站稳人机交互2.0语音交互场景
- asyncio之子进程交互
- 以太坊交互工具
- 学习 PixiJS — 交互工具
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Uberland
Alex Rosenblat / University of California Press / 2018-11-19 / GBP 21.00
Silicon Valley technology is transforming the way we work, and Uber is leading the charge. An American startup that promised to deliver entrepreneurship for the masses through its technology, Uber ins......一起来看看 《Uberland》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
HEX CMYK 转换工具
HEX CMYK 互转工具