简单设计并开发一个移动通信程序

时间:2021-11-04 23:36:29

网络越来越好,手机之间的互动已经是常态,王者荣耀、微信实时视频等,已经将多屏互动推到各到各种应用场景。
为了让大家能清楚地了解多屏互动,我将结合实例对移动设备实时通信进行研究,并系统性地呈现一些解决方案。
最开始,我尝试给大家展示如何建立一个最简单的点对点通信。
万事开头难,先假定一下需求:
局域网内通信。
写一个你看我画的程序。
纯客户端(一开始,我不打算让服务器参与)。

下面对需求进行进一步的分析。

需求分析

我做了一个简单的原型设计,如下图,其实真正的状态比这个稍复杂,这里提供一下 原型链接

需求原型

简单设计并开发一个移动通信程序

从原型上看,我们的流程应该是下图的形式。

流程图

简单设计并开发一个移动通信程序

下面我们进行架构设计与开发选型了。

架构设计

基于前文的需求假定进行简单设计网络模型,我将目标的网络分拆成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版的你看我画。

创建工程

在xcode上建立一个spritekit工程
简单设计并开发一个移动通信程序

简单设计并开发一个移动通信程序

在storyboard上加入按钮元素并绑定ViewController中的变量。
简单设计并开发一个移动通信程序

代码目录设计

简单设计并开发一个移动通信程序

framework为底层库,util为工具目录,adaptor为适配层。

实现绘图功能

创建一个自定义的view用来实现绘画。
简单设计并开发一个移动通信程序

在实现上我用最简单的绘图API, 不过为了区分对手与我画的,我用了2种颜色。
简单设计并开发一个移动通信程序

设置自定义鼠标响应事件
简单设计并开发一个移动通信程序

并在代码里创建1个scene用于加载自定义view.

上下层打通

object c 调用原生C++,我的做法是加一层代理。
我将代码结构设计如下
简单设计并开发一个移动通信程序

含义

CoreData 应用层协议结构
CoreAdaptor object c 适配
CoreDelegate 代码接口
Core c++适配

适配架构

简单设计并开发一个移动通信程序

在开发的时候,我希望上层在发送消息时,不需要指定IP与端口,而只需要索引就行,因此在framework层
建立一个 地址与索引的对应关系。
简单设计并开发一个移动通信程序

所以适配层调用接口只需要指定index就行了
简单设计并开发一个移动通信程序

应用层通信实现

结合之前设计的流程图,这个游戏过程的生命周期用viewcontroller 中的代码表示如下:
简单设计并开发一个移动通信程序

所以最终连接建立要么在confirm后,要么在onConfirm后。
简单设计并开发一个移动通信程序

简单设计并开发一个移动通信程序

start函数其实只是负责切换到画图场景
简单设计并开发一个移动通信程序

OK,整体代码写完后,我们来演示一下效果。

程序演示

先看下截图
简单设计并开发一个移动通信程序

没发现markdown能支持视频,所以只能将视频链接发上来:
http://v.youku.com/v_show/id_XMzA4MTQ0NjkzNg==.html?spm=a2h3j.8428770.3416059.1

我画的有点丑,不过没关系,大家可以上github上拉下来自己画。
https://github.com/70207/draw.git