Flex socket通信实践学习笔记(2)-1 - 转

Posted on 2012-03-16 16:43  tanceboy  阅读(397)  评论(0)    收藏  举报

面,阿堂和朋友们简单聊了一下,给出了一些FLEX和JAVA交互的操作图的界面一文(见 Flex socket通信实践学习笔记(1)),见阿堂的新浪空间(http://blog.sina.com.cn/s/blog_4c925dca0100s38p.html),由于阿堂平常一直比较忙,所以代码没有粘出来,今天我就给出该操作图相应的后台服务端java代码和客户端的FLEX代码.对于flex和JAVA的交互,我这里也简单总结一下,谈一下自己的看法!

1.flex和java交互时,需要三次握手过程,握手成功后,flex客户端会检测通信端口中是否存在安全策略文件,此时客户端发送以下报文内容.

<policy-file-request/>

服务端在收到该报文后,应该向客户端发送安全策略文件。

//Flex 安全策略文件

String xml ="<cross-domain-policy>";

xml = xml+"<site-control permitted-cross-domain-policies='all' />";

xml = xml+"<allow-access-from domain='*' to-ports='"+SERVER_PORT+"' />";

xml = xml+"</cross-domain-policy>";

之后再进行正常Socket通信.


所以,朋友们会在阿堂后面粘出来的代码中看到有如下代码

if(head.equals("<policy-file-request/>")){

pw.print(xml+"\0");

pw.flush();

}else{

...

}

代码的判断,对于安全策略文件,不是阿堂这篇文章讨论的重点,所以我就不再详细说明了。有兴趣的朋友可以继续看flex安全策略文件方面的介绍和说明,网上有很多这方面的介绍

2.flex和java交互中需要约定socket方面的通信协议或者说是rule.注意,这个是相对于开发的项目或游戏本身约定的通信协议,也就是说对于开发某个游戏,双方需要实际交互过程中,来议定的一些规则.如下面简单的网络聊天室,就议定了五类规则,0表示登录信息,1表示普通信息,2表示客户端总人数,3表示离开信息,4表示用户列表信息. (当然,我们用BlazeDS,apache mina等框架开发较为大中型的flash游戏时,我们议定的规则会比下述规则复杂得多.象我们部门其它项目组,开发的“德州扑克”,用的apache mina和flex交互时,就议定了2m-30种协议规则,每种规则传递的不是简单的数字,而是更为丰富的Array数组。java端是传递的JSon格式到flex客户端,flex端传递给java端是Array数组)但是,不管传递的数据有多复杂或者用什么框架,如BlazeDS,apache mina,FMS,Red5等,Flex和Java交互的基本原理都是一样的,只不过,用这些框架时,我们对于Socket的框架少了一些操作而也,很多的对于Socket框架方面的底层封装少了很多的操作,大大方便了Flex和服务端的交互.

(1)java后台服务端

//用户登录消息头

public static final String LOGIN_HEAD = "0";

//发送普通消息头

public static final String MESSAGE_HEAD = "1";

//客户端人数消息头

public static final String CLIENT_NUM_HEAD = "2";

//客户端离开消息头

public static final String LOGIN_OUT_HEAD  = "3";

//用户列表消息头

public static final String USER_LIST  = "4";


(2)flex客户端

// 用户登录消息头

public  static const LOGIN_HEAD:String = "0";

// 发送普通消息头

public  static const MESSAGE_HEAD:String = "1";

// 客户端人数消息头

public  static const CLIENT_NUM_HEAD:String = "2";

// 客户端离开消息头

public static const  LOGIN_OUT_HEAD:String= "3";

//如果是用户列表消息头

public static const USER_LIST:String = "4";


(3)Java服务端,主要用了三个类:一个主程序启动类启动监听服务MainServer.java,Socket线程处理类处理与每一个客户端的通信

SocketThread.java,广播类BroadCaster.java广我们的消息给所有的客户端。这实际上比较典型的设计模式--观察者设计模式。

(4)Flex客户端 logWin.mxml,SocketExample.mxml


服务端代码如下

代码如下

 

package com.chat.demo;


import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket;

import java.net.SocketAddress;


 

public class MainServer {

//监听端口

private static  final int SERVER_PORT = 9999;

//服务器

private ServerSocket server;

//广播者

private BroadCaster broadCaster = new BroadCaster();

public MainServer(){

}

 

 

 

public void startServer(){

//Flex 安全策略文件

String xml ="<cross-domain-policy>";

xml = xml+"<site-control permitted-cross-domain-policies='all' />";

xml = xml+"<allow-access-from domain='*' to-ports='"+SERVER_PORT+"' />";

xml = xml+"</cross-domain-policy>";

 

try{

//创建server端口 9999

server = new ServerSocket(SERVER_PORT);

System.out.println("---服务端启动成功 ! 监听端口:"+SERVER_PORT);

System.out.println("---等待客户端连接");

while(true){

//获得客户端连接socket

Socket socket = server.accept();

System.out.println("----客户端已连接:"+socket.getRemoteSocketAddress());

//Flash socket跨域访问设置

BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-


8"));

PrintWriter pw = new PrintWriter(socket.getOutputStream());

//接收用户名

char[] by = new char[22];

br.read(by,0,22);

String head = new String(by);

if(head.equals("<policy-file-request/>")){

pw.print(xml+"\0");

pw.flush();

}else{

//截取第一个字符串,判断消息类型

String cmd = head.substring(0, 1);

if(BroadCaster.LOGIN_HEAD.equals(cmd)){

//如果接收的是登录信息

String userName = head.substring(1, head.indexOf('\n'));

System.out.println(userName+" 上线了! ");

//将socket添加到广播对象 Vector中

broadCaster.add(socket);

//创建Scoket线程与客户端通信

SocketThread thread = new SocketThread(broadCaster,socket,userName);

//启动线程

thread.start();

 

//发送在线人数信息到客户端

broadCaster.sendClientNumToAll();

//发送登录信息给所有客户端

broadCaster.sendLoginInfoToAll(userName);

//将用户名添加到用户列表中

broadCaster.addUserName(userName);

//发送所有用户名列表,给所有客户端

broadCaster.sendUserListToAll();

}

}

}

}catch(IOException e){

e.printStackTrace();

}

}

 

public static void main(String[] args){

//启动Socket服务器

MainServer mainServer = new MainServer();

mainServer.startServer();

}

}


package com.chat.demo;


import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.net.Socket;


 

public class SocketThread extends Thread {

//保存广播引用

private BroadCaster broadCaster;

//用户名

private String userName;

//保存套接字引用

private Socket socket;

//套接字输入流

private BufferedReader br;

public SocketThread(BroadCaster broadCaster,Socket socket,String userName){

this.broadCaster = broadCaster;

this.socket = socket;

this.userName = userName;

}

 

public void run(){

try{

if(socket !=null && !socket.isClosed()){

//获取客户端发送过来的信息

br = new BufferedReader(new InputStreamReader(this.socket.getInputStream(),"UTF-8"));

//消息原始内容

String msg = null;

//消息头

String head = null;

//消息主体

String msgBody = null;

//发送的消息内容

String sendMsg = null;

while((msg = br.readLine()) != null){

head = msg.substring(0, 1);

msgBody = msg.substring(1);

if(BroadCaster.LOGIN_HEAD.equals(head)){

//如果是登录信息

sendMsg = msgBody+"登录了";

System.out.println(sendMsg);

//发送在线人数信息到客户端

broadCaster.sendClientNumToAll();

//用户登录消息,则发送用户登录消息到所有客户端

broadCaster.sendLoginInfoToAll(msgBody);

//发送所有用户列表给所有客户端

broadCaster.addUserName(msgBody);

//发送用户信息列表给所有客户端

broadCaster.sendUserListToAll();

}else if(BroadCaster.MESSAGE_HEAD.equals(head)){

System.out.println(msgBody);

//如果接收的是普通信息,则发送该消息到所有的客户端

broadCaster.sendMessageToAll(msgBody);

}

}

}

else{

System.out.println("---------连接已经关闭--------------");

}

}catch(Exception e){

}finally{

try{

//客户端离开了,移除广播者对象中的Socket

broadCaster.remove(socket);

//关闭输入流

if(br != null){

br.close();

}

//关闭Socket连接,释放资源

if(socket != null){

socket.close();

}

br = null;

socket = null;

System.out.println("客户端离开");

//发送当前在线人数消息给所有的客户端

broadCaster.sendClientNumToAll();

//移出用户登录名称

broadCaster.removeUserName(userName);

//发送用户离开消息给每一个客户端

broadCaster.sendLoginOutInfoToAll(userName);

//发送在线用户信息列表消息给所有客户端

broadCaster.sendUserListToAll();

}catch(IOException e){

}

}

}

}


 

package com.chat.demo;


import java.io.IOException;

import java.io.PrintWriter;

import java.net.Socket;

import java.util.Vector;


 

public class BroadCaster {

//用户登录消息头

public static final String LOGIN_HEAD = "0";

//发送普通消息头

public static final String MESSAGE_HEAD = "1";

//客户端人数消息头

public static final String CLIENT_NUM_HEAD = "2";

//客户端离开消息头

public static final String LOGIN_OUT_HEAD  = "3";

//用户列表消息头

public static final String USER_LIST  = "4";

//Socket 客户端连接用户名

private Vector<String> userNameVector = new Vector<String>();

//Scoket 客户端连接Vector

private Vector<Socket> socketVector = new Vector<Socket>();

public Vector<Socket> getSocketVector() {

return socketVector;

}

public void setSocketVector(Vector<Socket> socketVector) {

this.socketVector = socketVector;

}

 

//添加用户名

public void addUserName(String userName){

this.userNameVector.add(userName);

}

//删除用户名

public void removeUserName(String userName){

this.userNameVector.remove(userName);

}

//添加socket

public void add(Socket socket){

this.socketVector.add(socket);

}

//移除socket

public void remove(Socket socket){

this.socketVector.remove(socket);

}

//发消息给所有客户端

private void send(String head,String message){

PrintWriter pw = null; //输出流

Socket sock;

if(this.socketVector != null){

for(int i=0;i<this.socketVector.size();i++){

//获得每一个客户端连接

sock = this.socketVector.elementAt(i);

if(sock == null){

continue;

}

try{

//获得客户端输出流

pw = new PrintWriter(sock.getOutputStream(),true);

if(pw != null){

//输出msg信息到客户端

pw.println(head+message);

pw.flush();

}

}catch(IOException e){

e.printStackTrace();

}finally{}

}

}

}

 

//发送登录信息

public synchronized void sendLoginInfoToAll(String userName){

this.send(LOGIN_HEAD, userName);

}

//发送离线信息

public synchronized void sendLoginOutInfoToAll(String userName){

this.send(LOGIN_OUT_HEAD, userName);

}

//发送普通消息

public synchronized void sendMessageToAll(String message){

this.send(MESSAGE_HEAD, message);

}

 

//发送在线人数消息到所有客户端

public synchronized void sendClientNumToAll(){

if(this.socketVector != null){

this.send(CLIENT_NUM_HEAD, ""+this.socketVector.size());

}

}

 

//发送用户列表信息到所有客户端

public synchronized void sendUserListToAll(){

if(this.userNameVector !=null && this.userNameVector.size()>0){

StringBuffer sb = new StringBuffer();

//循环用户名列表生成消息

for(int i = 0;i<this.userNameVector.size();i++){

sb.append(this.userNameVector.elementAt(i));

//使用'-'作为用户名分隔符号,在Flex客户端中接收消息按此分隔获取用户名

sb.append("-");

}

if(sb.length()>0){

//去掉最后一个'-'分隔符号

sb.deleteCharAt(sb.length()-1);

}

//发送给所有客户端

this.send(USER_LIST, sb.toString());

}

}

}