簡單設計並開發一個移動通信程序
網絡越來越好,手機之間的互動已經是常態,王者榮耀、微信實時視頻等,已經將多屏互動推到各到各種應用場景。
為了讓大家能清楚地了解多屏互動,我將結合實例對移動設備實時通信進行研究,並係統性地呈現一些解決方案。
最開始,我嚐試給大家展示如何建立一個最簡單的點對點通信。
萬事開頭難,先假定一下需求:
局域網內通信。
寫一個你看我畫的程序。
純客戶端(一開始,我不打算讓服務器參與)。
下麵對需求進行進一步的分析。
需求分析
我做了一個簡單的原型設計,如下圖,其實真正的狀態比這個稍複雜,這裏提供一下 原型鏈接
需求原型
從原型上看,我們的流程應該是下圖的形式。
流程圖
下麵我們進行架構設計與開發選型了。
架構設計
基於前文的需求假定進行簡單設計網絡模型,我將目標的網絡分拆成3層:
網絡模型
對應的開發架構應該是這樣的
開發架構
開發規則
基於實時通信的高效性,我將底層庫的開發語言選擇了C++,協議格式選擇為二進製,網絡層協議選擇UDP(後麵會有切換TCP的選擇)。
通信協議端口我選擇12000.
字段 | 注解 |
---|---|
底層庫開發語言 | C++ |
協議格式 | 二進製 |
網絡層協議 | UDP |
通信端口 | 12000 |
下麵我將開始搭建底層庫(寫到這裏還是一行代碼都沒寫,不過現在是國慶節,既然有時間那就開始搞吧)。
底層庫搭建
創建工程
我設計的busi頭文件,給上層調用的。(詳細的見 github)
#ifndef hello_busi_hpp
#define hello_busi_hpp
#include <stdio.h>
namespace hello{
class BusiInterface{
public:
virtual int onInit(int myIp, int myPort);
virtual int onLink(int srcIndex, int srcIp, int srcPort, const char* srcName, int nameSize);
virtual int onConfirm(int srcIndex, const char* srcName, int nameSize);
virtual int onCancel(int srcIndex);
virtual int onMsg(int srcIndex, const char* msg, int size);
};
class Busi{
public:
Busi();
virtual ~Busi();
virtual int init(BusiInterface* itf);
virtual int link(const char* myName, int nameSize, int dstIp, int dstPort);
virtual int confirm(const char* myName, int nameSize, int index);
virtual int cancel(int index);
virtual int sendMsg(int index, const char* msg, int size);
private:
Busi* m_busi;
};
}
#endif /* hello_busi_h */
花了半天的時間寫完了底層庫,先來測試一下底層庫的連通性,我寫了一個程試程序,隻列下核心文件, 詳細請看github上。
void Test::testBusi()
{
m_busi = new Busi();
m_busi->init(this);
char ip[128];
printf("pleast input your name\n");
fgets(m_name, 127, stdin);
printf("please input your select\n");
printf("1 for link\n");
printf("2 for auto link\n");
int v;
scanf("%d", &v);
if(v == 1){
printf("please input the dst ip you want link\n");
scanf("%s", ip);
int dstIp = inet_addr(ip);
m_busi->link(m_name, strlen(m_name)+1, dstIp, HELLO_COMM_SERVER_LISTEN_PORT);
}
else{
printf("now you can want link from others\n");
}
}
void Test::sendMsg(const char *buffer, int size)
{
m_busi->sendMsg(m_dstIndex, buffer, size);
}
int Test::onInit(int myIp, int myPort)
{
struct in_addr addr;
addr.s_addr = myIp;
printf("on init, my ip:%s, my port:%d\n", inet_ntoa(addr), myPort);
return HELLO_STATUS_OK;
}
int Test::onLink(int srcIndex, int srcIp, int srcPort, const char* srcName, int nameSize)
{
struct in_addr addr;
addr.s_addr = srcIp;
printf("on link from ip:%s, port:%d, name:%s\n", inet_ntoa(addr), srcPort, srcName);
m_busi->confirm(m_name, strlen(m_name), srcIndex);
printf("now you can send msg to destination\n");
g_linked = 1;
m_dstIndex = srcIndex;
return HELLO_STATUS_OK;
}
int Test::onConfirm(int srcIndex, const char* srcName, int nameSize)
{
printf("on confirm from index:%d, name:%s\n", srcIndex, srcName);
m_dstIndex = srcIndex;
printf("now you can send msg to destination\n");
g_linked = 1;
return HELLO_STATUS_OK;
}
int Test::onCancel(int srcIndex)
{
printf("on cancel from index:%d\n", srcIndex);
return HELLO_STATUS_OK;
}
int Test::onMsg(int srcIndex, const char* msg, int size)
{
printf("on msg from index:%d, msg: size:%d\n", srcIndex, size);
printf("msg:%s", msg);
return HELLO_STATUS_OK;
}
因為我有一台mac, 一個ubuntu,所有測試時候,2邊都要編譯,mac是用xcode比較簡單,ubuntu上編寫makefile
framework:
BUSI_SRC=$(wildcard busi/*.cpp)
NET_SRC=$(wildcard net/*.cpp)
PACKAGE_SRC=$(wildcard package/*.cpp)
UTIL_SRC=$(wildcard util/*.cpp)
SRC=$(BUSI_SRC) $(NET_SRC) $(PACKAGE_SRC) $(UTIL_SRC)
OBJS=$(patsubst %.cpp, %.o, $(SRC))
CXXFLAGS += -D_WMD -pthread -std=c++11 -g -O0
LDFLAGS += -L/lib64 -pthread
LIB=../lib/libwmd.a
default: $(LIB)
$(LIB): $(OBJS)
rm -rf $@
ar -rs $@ $(OBJS)
clean:
-rm -rf $(OBJS)
.cpp:
$(CXX) -o $@ $< $(CXXFLAGS) $(LDFLAGS) $(LIBRARY) $(LIBS)
test
SRC=$(wildcard *.cpp)
OBJS=$(patsubst %.cpp, %.o, $(SRC))
CXXFLAGS += -std=c++11 -g -O0
LDFLAGS += ../lib/libwmd.a -L/lib64 -pthread
APP=./hello.out
default:$(APP)
clean:
-rm -rf $(OBJS)
-rm -rf $(APP)
$(APP): $(OBJS)
$(CXX) -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
.cpp:
$(CXX) -g -o $@ $< $(CXXFLAGS) $(LDFLAGS) $(LIBRARY) $(LIBS)
現在我們來實測一下連通性:
我讓mac做主動連接的一方,讓linux做被動連接。
可以看到2者已經通了,咱們的底層庫搭建OK!
感興趣並助喜歡動手同學,可以 下載代碼 實測一下。
寫一個你看我畫的程序吧
為了方便調試,我選擇開發一個mac版的你看我畫。
創建工程
在storyboard上加入按鈕元素並綁定ViewController中的變量。
代碼目錄設計
framework為底層庫,util為工具目錄,adaptor為適配層。
實現繪圖功能
在實現上我用最簡單的繪圖API, 不過為了區分對手與我畫的,我用了2種顏色。
並在代碼裏創建1個scene用於加載自定義view.
上下層打通
object c 調用原生C++,我的做法是加一層代理。
我將代碼結構設計如下
含義
CoreData | 應用層協議結構 |
---|---|
CoreAdaptor | object c 適配 |
CoreDelegate | 代碼接口 |
Core | c++適配 |
適配架構
在開發的時候,我希望上層在發送消息時,不需要指定IP與端口,而隻需要索引就行,因此在framework層
建立一個 地址與索引的對應關係。
應用層通信實現
結合之前設計的流程圖,這個遊戲過程的生命周期用viewcontroller 中的代碼表示如下:
所以最終連接建立要麼在confirm後,要麼在onConfirm後。
OK,整體代碼寫完後,我們來演示一下效果。
程序演示
先看下截圖
明天我上傳下視頻,我畫的有點醜,不過沒關係,大家可以上github上拉下來自己畫。
https://github.com/70207/draw.git
最後更新:2017-10-17 17:34:09
上一篇:
redis4.0之module API
下一篇:
寫一個簡單的實時互動小遊戲
幹掉 Whatsapp,隻需4000個微笑?
WIN7安裝IIS後無默認網站(Default Web site)解決方案
數據庫默認端口和驅動總結
setTimeout和setInterval的使用
【Oracle12.2新特性掌上手冊】-第八卷 PDB的快速創建與移除
AI 產學研大集結,CCAI 2017 中國人工智能大會將於明日正式召開(附參會指南)
7月25日雲棲精選夜讀:刷臉out了?阿裏“刷腳購物”亮相CVPR 2017
如何在 Linux 中啟用 Shell 腳本的調試模式
收中華人民共和國稅收征收管理法(主席令第四十九號) 2015年8月15日 - 會關於修改〈中華人民共和國文物保護法〉等十二部法律的決定》(主席令第...第八十九條 納稅人、扣繳義務人可以委托稅務代理人代為辦理稅務事宜。 第...起相關人物 祁中華人民共和國
互聯網企業安全高級指南2.1 創業型企業一定需要CSO嗎