最近有個基於tcp socket 協議和設備交互需求,想到了新生命團隊的各種組件,所以決定用NewLife網絡庫作為服務端來完成一系列的信息交互.
第一,首先說一下我們需要實現的功能需求吧
1,首先客戶有一堆自動售貨機的設備,設備連接socket服務端后 定時發送設備實時狀態作為心跳信息,並且服務端需要下發信息予以確認。
2,需要知道設備的實時在線狀態
3,設備需要實現微信,支付寶掃碼支付需求,當客戶買東西的時候選擇掃碼支付時,設備上報產品價格信息,支付方式,服務器下發微信或者支付寶的當面付二維碼。
4,當客戶掃碼支付后,服務器需要下發支付結果。
5,設備上報出貨成功或失敗信息,服務器予以回復確認
6,設備上報現金收款的收款或者退款信息,服務器保存並且下發回復確認。
7,服務器后台需要能夠主動的向設備推送廣告信息,並且能夠實現掉線設備的廣告推送
第二,我們來分析這個可靠的服務器的大致結構是如何的(僅包含設備實時狀態和支付交互)
1,想要服務穩定可靠,那么首先考慮的是程序的運行方式,當然是以一個windows服務為宿主的方式來運行最為可靠,那么我們可以使用TopShelf來構建這樣一個服務宿主
2,實現設備的在線狀態實時可知,我們需要定時的去判斷這個設備是否在定時給服務器發送心跳信息,這個項目中我們以超時未發送心跳信息為標准來衡量他是否在線。
3,設備數據上報涉及到粘包的處理,設備上報的數據是怎么樣一個協議,如何來解決粘包的問題,這里我們借助Newlife網絡庫的管道模式來處理粘包
4,微信支付寶的掃碼支付如何及時知道客戶付款狀態,並且及時的下發給設備。
第三,我們需要用到的第三方類庫有哪些
1,首先使用的網絡庫是Newlife 新生命團隊的網絡庫
2,構建windows服務使用的是TopShelf
3,狀態的信息保存使用Redis,這里我們使用CSRedisCore 這個類庫
4,微信支付寶本次使用的是PaySharp這個庫,二維碼生成使用QrCoder庫
第四,開始構建的基本的Socket服務吧
1,首先創建一個windows服務,引入Topshelf后
static void Main(string[] args) { var rc = HostFactory.Run(x => //1 { x.Service<DianSocketMain>(s => //2 { s.ConstructUsing(name =>new DianSocketMain()); //3 s.WhenStarted(tc => tc.Start()); //4 s.WhenStopped(tc => tc.Stop()); //5 }); x.RunAsLocalSystem(); //6 x.SetDescription("售貨機socket服務端"); //7 x.SetDisplayName("Vending machine socket"); //8 x.SetServiceName("Vendingmachinesocket"); //9 x.OnException(s => { LogerHelper.WriteErroLog("test",s.Message); }); }); //10 }
DianSocketMain類就是具體的Socket服務端,來看下是如何啟動一個基本的socket服務端
public class DianSocketMain { /// <summary> ///網絡庫服務端 20181225 /// </summary> private NetServer _newLifeServer; /// <summary> /// /// </summary> public void Start() { try { try { //redis初始化 string redisHost = CoomHelper.GetAppSettings("RedisHost", "127.0.0.1:6379"); string redisPwd = CoomHelper.GetAppSettings("RedisPwd", ""); string prefix = "{device_server}:"; string redisConn = $"{redisHost},password={redisPwd},defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix={prefix}"; var csredis = new CSRedis.CSRedisClient(redisConn); RedisHelper.Initialization(csredis); } catch (Exception e) { XTrace.WriteLine(e.Message); } //啟動客戶端 StartNewLifeListenClient(); } catch (Exception e) { XTrace.WriteException(e); } } /// <summary> /// 停止win服務 /// </summary> public void Stop() { try { if (_newLifeServer != null) { _newLifeServer.Dispose(); } } catch (Exception e) { XTrace.WriteException(e); } } /// <summary> /// 監聽客戶端 /// </summary> private void StartNewLifeListenClient() { try { XTrace.WriteLine("當前tcp端口號為:" + 9046); //創建監聽地址和端口 IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); var svr = new NetServer(ipAddress, 9046, NetType.Tcp) { Log = XTrace.Log, SessionTimeout = 60 }; svr.Add<ReciveFilter>();//粘包處理管道 svr.Received += new EventHandler<ReceivedEventArgs>(NewlifeRecive);//數據接收 //svr.Error += new EventHandler<ExceptionEventArgs>(NewlifeError);//錯誤處理 //svr.OnDisposed+=new EventHandler(); svr.Start(); _newLifeServer = svr; //XTrace.WriteLine("會話超時時間為:"+svr.SessionTimeout); } catch (Exception ex) { XTrace.WriteLine(ex.Message); } } /// <summary> /// 數據接收 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void NewlifeRecive(object sender, ReceivedEventArgs e) { INetSession session = (INetSession)sender; var pk = e.Message as Packet; if (pk.Count == 0) { XTrace.WriteLine("數據包解析錯誤"); return; } } }
這里包含的一個有redis的初始化,粘包處理,數據接收的一個基本服務端,下面我們再講如何解決粘包的問題。