欢迎加入开发群:285275050
交流
游戏如果脱离了网络,那么它的可玩性将会大大降低,其生命周期也相对较短,小鸟与水果就把它当成神话好了。本人也是最近接触网络游戏的开发,写这个主要是记录一下自己的学习过程,同时也为以后的开发做个热身运动,至于写到哪里写到什么程度,我也没有确切打算,当然最好是能对立出一个小的游戏工程。
从网上查资料了解到网络游戏的网络连接部分主要有tcp连接和udp连接,由于对游戏的要求不同选择哪种,要根据实际情况,通常局域网对战类游戏为udp,因为局域网内数据传输相对稳定。在这里服务器端选用的开发语言为java,而客户端选用的开发语言为cocos2d-x的开发语言为c++。对于socket编程,cocos2dx没有一如第三方的开发库,这样要实现跨平台的socket我们就只能选用BSD
Socket。BSD
Socket不需要引入第三方开发库,各个平台基本都支持。不过有些遗憾的BSD
Socket不符合面向对象的编程原则(为c语言),不过还好,我们可以自己去集成第三方开发库。在这里我选用了第三方的开发库socketcc和pthreadcc,他们是对bsd
socket的一个c++封装,我已经成功在android和ios下编译。如果对如何在这两个平台引入和使用这两个库,可以看我前面的文章,在那里我有详细的介绍。
好了,今天我们险通过tcp连接,并通过tcp来传送数据来把客户端输入的用户名密码提交给服务器。Ok,开始吧!
首先看客户端界面
http://s12/mw690/6084f588t7b5d1e4468bb&690
界面代码:
void LoginScene::loadUI(){
CCSize editBoxSize = CCSizeMake(200, 60);
_pEditName = CCEditBox::create(editBoxSize, CCScale9Sprite::create("green_edit.png"));
_pEditName->setPosition(ccp(512, 680));
_pEditName->setFontColor(ccRED);
_pEditName->setPlaceHolder("Name:");
_pEditName->setMaxLength(8);
_pEditName->setReturnType(kKeyboardReturnTypeDefault);
_pEditName->setDelegate(this);
addChild(_pEditName);
_pEditPwd = CCEditBox::create(editBoxSize, CCScale9Sprite::create("green_edit.png"));
_pEditPwd->setPosition(ccp(512, 600));
_pEditPwd->setFontColor(ccRED);
_pEditPwd->setPlaceHolder("Password:");
_pEditPwd->setMaxLength(8);
_pEditPwd->setReturnType(kKeyboardReturnTypeDone);
_pEditPwd->setDelegate(this);
addChild(_pEditPwd);
}
当出发提交时执行:
void
SocketClient::login(const char * userName,const char *
userPwd){
if
(userName==NULL||userPwd==NULL) {
return;
}
if
(strlen(userName)>0&&strlen(userPwd)>0)
{
_tcpSocket->SendASCII(userName);
_tcpSocket->SendASCII(userPwd);
}
}
服务器端:
在main中调用start方法,start方法当有客户接入时,开始一个客户端线程用于接收客户端发来的数据。
public
void start(){
ServerSocket ss=null;
try {
ss=new ServerSocket(9999);
} catch (Exception e) {
e.printStackTrace();
}
while(true){
Socket clientSocket=null;
try {
clientSocket=ss.accept();
System.out.println("a client connected!");
DataInputStream dis=new
DataInputStream(clientSocket.getInputStream());
byte bytesRead[]=new byte[20];
dis.read(bytesRead);
String strValue=new String(bytesRead);
System.out.println("receiveValue="+strValue);
new Thread(new TCPClientThread(clientSocket)).start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
内部线程类:
private
class TCPClientThread implements Runnable {
Socket cSocket=null;
public TCPClientThread(Socket cliSocket) {
super();
this.cSocket = cliSocket;
}
public void run() {
while(cSocket!=null){
DataInputStream dis=null;
try {
dis=new DataInputStream(cSocket.getInputStream());
byte buf[]=new byte[60];
dis.read(buf);
System.out.println(new String(buf));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
当收到数据时会将收到的数据打印。
运行服务器并提交数据:
http://s5/mw690/6084f588t7b5d1e5ab434&690
可以看到服务器接收到了我闷得数据,
这时是否会感觉奇怪,我明明发了两次,
void
SocketClient::login(const char * userName,const char *
userPwd){
if
(userName==NULL||userPwd==NULL) {
return;
}
if
(strlen(userName)>0&&strlen(userPwd)>0)
{
_tcpSocket->SendASCII(userName);
_tcpSocket->SendASCII(userPwd);
}
}
而且服务器也是没接收一次就输出一行,为什么服务器只接收了一次。
其实这是不可避免的,服务器接收时是以网络字节流来接收的,这是就不可避免的产生分包和粘包等问题,为了能正确接收数据我们就得自己去定义消息,并按消息定义原则自己对收到的数据重新组装。
今天先介绍到这。
加载中,请稍候......