在对Flash Media Server(FMS)研究一段时间之后,发觉普通的网络实时通信使用FMS并不是最佳的选择,原因在于FMS是一种收费服务端软件,对一般用户来说,这种花费会带来很重的负担,其次,FMS是强大的视频语音服务端,如果单有文字方面的实时通信需求的话,就不必花费额外的资金、时间、精力去搭建FMS服务。Flash8的ActionScript(AS) 类中有个非常有用的XMLSocket 类,XMLSocket可为基于字符串的实时通信提供解决方案。Flash8中的帮助文档是这样写的:XMLSocket 聊天解决方案保持与服务器的开放连接,这一连接允许服务器即时发送传入的消息,而无需客户端发出请求。以下我们就来探讨客户端与服务端的实现。
在工作中我使用最多的语言是C#,服务端用C#实现起来也容易得多,网上也有C# Socket通信的例子,我们在此基础上加以改进(我一向喜欢站在巨人的肩膀上),以便更好地与Flash结合。
C# Flash Socket服务端主程序:
FS.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Xml;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Runtime.Remoting.Messaging;
namespace FSocket
{
public partial class FS : ServiceBase
{
internal static Hashtable clients = new Hashtable();
private TcpListener listener;
static int MAXNUM = 1000000;
static string initPort;
internal static bool SocketServiceFlag = false;
//用于线程同步,初始状态设为非终止状态,使用手动重置方式。
private EventWaitHandle allDone = new EventWaitHandle(false, EventResetMode.ManualReset);
public FS()
{
InitializeComponent();
}
/// <summary>
/// 服务启动时读取Socket端口设置,监听线程开始
/// </summary>
/// <param name="args"></param>
protected override void OnStart(string[] args)
{
initPort = LoadXML();//读取socket端口号
int port = getValidPort(initPort);
if (port < 0)
{
return;
}
string ip = this.getIPAddress();
try
{
IPAddress ipAdd = IPAddress.Parse(ip);
listener = new TcpListener(ipAdd, port);
listener.Start();
FS.SocketServiceFlag = true;
ThreadStart thread = new ThreadStart(StartSocketListen);
Thread mythread = new Thread(thread);
mythread.Start();
writeLog("FSocket正常启动");
}
catch (Exception ex)
{
writeLog("异常错误:" + ex.ToString());
}
}
/// <summary>
/// 服务结束时清除相关资源
/// </summary>
protected override void OnStop()
{
FS.SocketServiceFlag = false;
System.Collections.IEnumerator myEnumerator = FS.clients.Values.GetEnumerator();
string msgUsers = GetUserList();
while (myEnumerator.MoveNext())
{
Client client = (Client)myEnumerator.Current;
client.SendToClient(client, "CLOSE|服务重启,请重新登录。\0");
Thread.Sleep(100);
}
FS.clients.Clear();
listener.Stop();
writeLog("FSocket服务关闭");
}
private string getIPAddress()
{
IPAddress[] AddressList = Dns.GetHostByName(Dns.GetHostName()).AddressList;
if (AddressList.Length < 1)
{
return "";
}
return AddressList[0].ToString();
}
private int getValidPort(string port)
{
int lport;
try
{
if (port == "")
{
throw new ArgumentException("端口号为空,服务启动不成功");
}
lport = System.Convert.ToInt32(port);
}
catch (Exception e)
{
return -1;
}
return lport;
}
private void StartSocketListen()
{
while (FS.SocketServiceFlag)
{
try
{
if (listener.Pending())
{
Socket socket = listener.AcceptSocket();
if (clients.Count >= MAXNUM)
{
socket.Close();
}
else
{
Client client = new Client(this, socket);
ThreadStart thread = new ThreadStart(client.ServiceClient);
Thread clientService = new Thread(thread);
clientService.Start();
writeLog("FSocket开始监听来自IP:" + client.IpAddress + "客户:" + client.Name + "的
信息。");
}
}
Thread.Sleep(200);
}
catch (Exception ex)
{
writeLog("异常错误:" + ex.ToString());
}
}
}
public string GetUserList()
{
string Rtn = "";
System.Collections.IEnumerator myEnumerator = FS.clients.Values.GetEnumerator();
while (myEnumerator.MoveNext())
{
Client client = (Client)myEnumerator.Current;
if (client.UserType == "FSUser")
{
Rtn = Rtn + client.ClientID + "," + client.Name + "|";
}
}
Rtn = Rtn.Trim('|');
return Rtn;
}
public string GetGuestList()
{
string Rtn = "";
System.Collections.IEnumerator myEnumerator = FS.clients.Values.GetEnumerator();
while (myEnumerator.MoveNext())
{
Client client = (Client)myEnumerator.Current;
if (client.UserType == "Guest")
{
Rtn = Rtn + client.ClientID + "," + client.Name + "|";
}
}
Rtn = Rtn.Trim('|');
return Rtn;
}
/// <summary>
/// 写日志,跟踪服务端信息
/// </summary>
/// <param name="logTxt">日志内容</param>
public void writeLog(string logTxt)
{
ServerLog sLog = new ServerLog();//日志
sLog.FileLogPath = System.AppDomain.CurrentDomain.BaseDirectory + "\\ServerLog\\";
sLog.LogFileName = "ServerLog_" + DateTime.Now.Year + DateTime.Now.Month + DateTime.Now.Day + ".txt";
sLog.WriteLog(logTxt);
}
/// <summary>
/// 读取socket端口设置
/// </summary>
public string LoadXML()
{
XmlDocument XMLDOC = new XmlDocument();
XMLDOC.Load(System.AppDomain.CurrentDomain.BaseDirectory + "\\ServerPort.xml");
XmlNode node = XMLDOC.DocumentElement.ChildNodes[0];
return node.Value;
}
/// <summary>
/// 连接客户类
/// </summary>
public class Client
{
private string clientID;
private string name;
private string userType;
private Socket currentSocket = null;
private string ipAddress;
private FS server;
private string state = "closed";
public string clientCommand;
public delegate void ServiceClientDelegate();
public delegate void SendToClientDelegate(Client client, string msg);
public Client(FS server, Socket clientSocket)
{
this.server = server;
this.currentSocket = clientSocket;
ipAddress = getRemoteIPAddress();
}
public string ClientID{get{return clientID; }set{clientID = value; }}
public string Name{get{return name; }set{name = value; }}
public string UserType{get{return userType; }set{userType = value; }}
public Socket CurrentSocket{get{return currentSocket; }set{currentSocket = value; }}
public string IpAddress{get{return ipAddress; }}
public string getRemoteIPAddress()
{
return ((IPEndPoint)currentSocket.RemoteEndPoint).Address.ToString();
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="result"></param>
public void SendToClientCallback(IAsyncResult result)
{
AsyncResult asyncobj = (AsyncResult)result;
string sendToClientName = (string)asyncobj.AsyncState;
SendToClientDelegate STC = (SendToClientDelegate)asyncobj.AsyncDelegate;
try
{
STC.EndInvoke(result);
}
catch (Exception err)
{
}
}
public void SendMSG(Client client, string msg)
{
System.Byte[] message = System.Text.Encoding.UTF8.GetBytes(msg.ToCharArray());
client.CurrentSocket.Send(message, message.Length, 0);
}
public void SendToClient(Client client, string msg)
{
SendToClientDelegate sendToClient = new SendToClientDelegate(SendMSG);
sendToClient.BeginInvoke(client, msg, SendToClientCallback, null);
}
/// <summary>
/// 接收数据
/// </summary>
/// <param name="result"></param>
private void ServiceClientCallback(IAsyncResult result)
{
AsyncResult asyncobj = (AsyncResult)result;
string serviceClientName = (string)asyncobj.AsyncState;
ServiceClientDelegate SC = (ServiceClientDelegate)asyncobj.AsyncDelegate;
try
{
SC.EndInvoke(result);
}
catch (Exception err)
{
}
}
public void SocketMSG()
{
string[] tokens = null;
byte[] buff = new byte[1024];
bool keepConnect = true;
while (keepConnect && FS.SocketServiceFlag)
{
tokens = null;
try
{
if (currentSocket == null || currentSocket.Available < 1)
{
Thread.Sleep(300);
continue;
}
int len = currentSocket.Receive(buff);
clientCommand = (System.Text.Encoding.UTF8.GetString(buff, 0, len)).TrimEnd('\0');//把得
到的数据用ASCII的编码形式读出解决中文的显示问题
tokens = clientCommand.Split(new char[] { '|' });
if (tokens == null)
{
Thread.Sleep(200);
continue;
}
}
catch (Exception e)