今天晚上11点半,在学校断网后的十分钟,我终于实现了C++服务器端与Android客户端的通信。本来很简单的一件事,却因为Android
好了不多说,直接进入主题:
C++
用C++进行网络编程有很多方式,比如可以使用winSock,winInt,或者使用MFC的封装类CSocket等等。这里我使用的是比较简单的CSocket类来实现。
这里先简单说说使用CSocket类来建立服务器端的过程:
上图是C++服务器端与Android客户端通信的流程图。
看到上面的流程图,C++程序员应该感到高兴,这不就是CSocket客户端的变体嘛。服务器端完全没变化,这先不说,Android客户端连connet动作都省下来了。
这……是不是写错了?
没错,确实连connet这个步骤都省了。当然,没有connect只不过是因为这个connect的动作直接被封装到了Android Socket的new操作里而已。不然,服务器端怎么可能知道你这个狡猾的客户端会不会三更半夜的来敲门,说要提供服务啊。哈哈。
注:在Android Socket的构造函数中有多种形式,并不是每一种形式都必须在构造时就连接到服务器。读者可以自己阅读Android的开发文档,你会发现Socket也有个名为Connect的成员函数。
好了,有了基本的操作流程后,写起代码来就容易多了。
观察流程图中C++服务器端的构造过程可以知道,服务器端的构造大致分为三大步骤:
1、创建用于侦听的socket套接字sevSock并开启侦听;
2、创建用于接收/发送信息的socket套接字reveiceSoc,创建后“绑定”到侦听套接字;
3、reveiceSoc套接字接收/发送信息。
这几个步骤对于C++程序员来说已经是滚瓜烂熟了。
我也不多说,直接动手更见效。
(开发环境:VS 2008)
第一步:创建一个新MFC应用程序项目,这里名为AndroidSocket,应用程序类型为“基于对话框”就可以了。并且在创建过程的向导中的高级功能的选项中选中windows套接字。
第二步:添加用于侦听的socket套接字。为项目添加一个MFC类用于侦听客户端的连接,并令该类的基类为CSocket。本例中该类的类名为CAndroidSoc。
第三步:添加用于接收/发送信息的socket套接字。再为项目添加一个MFC类用于接收/发送信息,该类也要继承于CSocket。本例中该类的类名为CRevSocket。
第四步:为对话框类添加成员变量CAndroidSoc m_androidSoc,,并在OnInitDialog()函数中添加如下代码:
m_androidSoc.Create(8005,SOCK_STREAM);//创建侦听套接字
m_androidSoc.Listen();//开启侦听
第五步:重写“侦听”套接字CAndroidSoc的虚函数OnAccept()。并在OnAccept的实现中添加如下代码:
CRevSocket* pRveSocket=new CRevSocket(); //创建用于接收/发送信息的套接字
this->Accept(*pRveSocket);//让“侦听”套接字绑定“接收/发送信息”的套接字
第六步:重写“接收/发送信息”套接字CRevSocket的虚函数OnReceive()。并在该函数的实现中添加如下代码:
wchar_t* wch=new wchar_t[1024*4]; //申请缓冲区
memset(wch,0,1024*4); //清空缓冲区
this->Receive(wch,1024*4); //接收客户端发送过来的信息
AfxMessageBox(wch); //显示信息(这里只不过用于测试用户发送过来的信息而已)
Delete wch; //释放空间
实现了以上六个步骤,服务器端就算是建好了。当然,上面只是简单的建立了服务器端接收客户端发送过来的信息的而已。至于服务器端向客户端发送信息的步骤也非常简单,我就不多说了。百度一搜就一大堆。
接下来就讲讲Android客户端的创建:
如果读者对Android编程有一些基础的话,看到上面的客户端建立的流程图,心中肯定会暗喜:oh,my lady gaga.这花不了哥(姐)10分钟。可是,十分钟过去了,二十分钟过去了,甚至到了饥肠辘辘,这个手机还是不能成功的发送一条信息到服务器去。检测了代码十几遍,没错啊。也是按照图上的流程来做的,不会流程图有问题吧?
哥(姐),恭喜你,你怀疑对了。学习就是需要怀疑,不然怎么又突破。我记得我小学老师曾这么教训我们这些每次作业都抄书上答案的同学说:“读书不可尽信书,尽信书,不如无书!”。这话啊,当头棒喝,我们当即就遵循了“无书”的真理。哈哈,开个玩笑。
不过你想得确实没错。确实是流程图有问题。不过这个问题并不是像少了Connect操作这么明显(当然,这个步骤本来就不需要)。而是进入了线程的问题。
大家应该知道吧。使用Android的Socket类来开发网络通信应用,意味着你用的是TCP协议进行网络通信。而TCP协议是一种面向连接的通信方式。何为面向连接,简单的说就是你的客户机必须先和服务器进行了连接,连接了还不行,还要一直占用一条通信电路(当然,这个概念并不是占用了一条电线这么简单),直到你主动或者被动地与服务器断开连接。而在整个连接过程中,你的程序就一直在等待,这就是阻塞了。那么程序在等待什么?等待爱情?当然不,它是在等待服务器的响应。
其实,对于这个Android的Socket开发,刚开始时我也是很郁闷。在编写一个简单的Scoket通信应用时,我参考了不少书,并且也按书上写的照打了一遍,可是结果不是直接崩溃就是根本通不了信,服务器连个连接的请求都没有接收到。
好吧。既然都已经说了这么多,是应该告诉解决方法了。其实方法也是很简单,就是新建一个线程,将Socket的操作放到该线程上。
解决方法:新建线程,代码如下:
//在OnCreate函数中添加
Try //创建线程,并通过Socket与服务器建立连接
{
Thread thread=new Thread(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
try{
s=new Socket("192.168.0.105",8005);
outputStream=s.getOutputStream();
}
catch(Exception e){
e.printStackTrace();
}
}
});
thread.start();
}
catch(Exception e)
{
e.printStackTrace();
}
//在OnCreate函数中添加按钮响应函数,
//实现用户按下按钮,就想服务器端发送“SUCCEEDED!”信息
ITButton=(Button)findViewById(R.id.BuildingIB);
ITButton.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
byte[] buffer=new byte[4*1024];
String strbuffer="succeeded!";
buffer=strbuffer.getBytes();
try {
outputStream.write(buffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
好了!Android客户端的编写也算是完成了。虽然不详细,不过大家应该看得懂。