即时通 系统 - 客 端 - lash ocket
2010-02-27 09:38 宝宝合凤凰 阅读(4015) 评论(1) 编辑 收藏 举报简介
本教程将描述从头开始构建一个web即时通讯系统的几个主要过程,以及它们是如何结合在一起的。
在这篇教程中,将学习flashsocket、policyserver、javascript和flash的互操作、swf的多语言编译器haxe、sessionserver的三种模式以及各自的优点与缺点。
本教程会在完成基础的web im通讯系统以后,深入的讨论如何构建高性能的和分布式的通讯系统。
目录(有待更新)
概述
基础
客户端-flash
客户端-javascript
服务器端-policyserver
服务器端-sessionserver-select模式
服务器端-sessionserver-多线程模式
服务器端-sessionserver-异步模式
...
预备知识
本教程的服务器端使用c# 2.0编码,客户端使用flash来完成通讯,并使用js来描述浏览器端行为。
页面刷新
页面刷新即通过刷新页面来更新聊天记录,通过页面提交来发送消息。毫无疑问,这种实现是最简单的,也是效率最差的。它意味着每一次收发消息都对应了一次httprequest,而http作为应用层协议,连接代价是相当大的。
ajax
ajax可以理解为xmlhttprequest,本质与页面刷新相同,但是页面上会有很多资源文件,比如图片、css、js、flash,这些文件会随着页面刷新并且没有做浏览器端缓存的时候更新。为了接收或者发送数十字节的消息而更新可能多达数百K字节的字节文件,显然是很愚蠢的。所以使用ajax来通讯,"只"需要为消息的获取或者发送而进行请求。ajax从这一点上是优于页面刷新的。
comet/server push
ajax为每一个消息的发送和接收建立一个新的http连接,并在短暂通讯完毕以后关闭。而server push的本质是http长连接,它的思路是,让服务器和浏览器维持着一个时间跨度很长的http连接,这个http连接可能大部分时候并不需要做什么,只在有消息的时候,向浏览器写入。所以这种方式避免了建立以及关闭http连接时大量的开销,提高了资源利用率。不过server push也有它的问题,在一个web服务器上,http连接数并非没有限制,所以如果把http连接数也看作是系统资源的话,server push确实是对于此资源的极大浪费了。
comet是server push的思路上的一种改进,它在ajax和server push之间使用了一种折中的策略,在高频率的通讯过程中,comet使用http长连接。一旦通讯变得不频繁,comet便释放http连接,并在下一次通讯的时候,是情况而定,看是否需要建立http长连接。
server push在asp.net中可以通过下面的代码片段实现:
protected void Page_Load(object sender, EventArgs e)
{
while (IsSessionEnd as bool)
{
lock (token as object)
{
Response.Write(message as string);
Response.Flush();
}
}
}
(当然,在实际编码中,as type的写法是不必要的,这里只是为了说明类型)
可以看到,在上面的代码片段中,会话结束前,Response.End()都不会被调用。
socket/tcp
在页面刷新、ajax和server push的思路里,最重要的一个共同点就是基于http。http是web的基础,然而http并不符合即时通讯系统的思想,很大程度上是由于http几个特点:
[
http是无状态的,
http是一问一答的,
http是单播的,
http是应用层协议,通讯代价很大
]
然而http的下层协议tcp/udp就很适合用来做为即时通讯系统的通讯协议。可是在web标准里,一切都是基于http的。幸运的是,我们可以通过浏览器插件的信路来完成通讯,在java applet、flash和silverlight里,都提供了socket/tcp,并且dom和flash或silverlight是可以互操作的。这就意味着,dom也可以间接的使用socket来与服务器通讯。
摘要
在上一篇"基础-web即时通讯系统的四种实现"中,我们讨论了socket/tcp相对于其他基于http的通讯方式的优越。本文则详细描述如何使用flash/actionscript,来实现socket/tcp,并使用开源的haxe来编译它。
flash中的socket
socket的构造
实例化一个socket对象,只需要一行代码:
static var socket = new flash.net.Socket();
主动方法和被动方法
而剩下的工作便是把这个对象包装成合适接口供javascript调用了。这时候主要有两类方法,一类是主动性方法,由客户端直接发起的便是主动方法,比如connect、send就是主动方法。
另一类是被动方法,也就是服务器端发起,然后在客户端触发事件,并被调用的回调方法,比如connected、receive就是被动方法。
主动方法和被动方法直接决定了代码的结构,决定了javascript和flash的互操作方式。
事件时序
我们从事件时序上来看一下flash中socket的可能的一个过程:
flash加载完毕
我们先注册一个回调,在flash加载完毕的时候,获得一个通知,以便为下面的连接做一些设置和准备。
flash.external.ExternalInterface.call("setTimeout", jsScope + "loaded()");
建立一个socket连接
接着我们便使用下面的方法来建立一个socket连接
static function connect(host : String, port : String) {
trace("Connecting to socket server at " + host + ":" + port);
socket.connect(host, Std.parseInt(port));
}
这里的两个形参:host为ip或者域名,port为通讯的端口。trace为输入到flash控制台的方法。
flash socket在端口上是有限制的,仅被允许访问4502-4534这个网段里的端口。
flash socket在执行socket.connect(host, Std.parseInt(port))时,无论如何,会先访问843端口,如果843端口被监听,并且在连接上后接收到一个policy-file.xml的话,才会断开与843端口的连接,并与调用方法时指定的端口连接。
如果flash没有正常得到这个policy-file.xml的话,从调用方法时指定的端口连接时刚连接上便获得这个policy-file.xml也是可以的。如果这两种情况下都没有获得policy-file.xml,那么通讯是不能继续的。这是flash特有的安全机制。
关于这个安全机制,以及提供这个policy-file.xml服务器程序policyserver,我会在稍后的教程里详细介绍。
连接失败
如果flash没有顺利连接上服务器,或者超时,我们可以注册一个回调来获得连接失败的通知,已准备重连。
socket.addEventListener(flash.events.SecurityErrorEvent.SECURITY_ERROR, function(e) : Void {
trace("SECURITY ERROR : " + e.text);
flash.external.ExternalInterface.call("setTimeout", jsScope + "securityError('" +e.text+ "')", 0);
}
);
连接成功
如果顺利连接上了,这时候,我们可以通过下面的代码来注册一个回调,得到连接状态更新的通知。
socket.addEventListener(flash.events.Event.CONNECT, function(e) : Void {
trace("Connected to server");
flash.external.ExternalInterface.call("setTimeout", jsScope + "connected()", 0);
}
);
发送消息
下面我们便可以从flash发送消息给服务器了
static function send(msg) {
if (socket.connected) {
trace("Writing '" + msg + "' to server");
//socket.writeMultiByte(msg,"gb2312");
socket.writeUTFBytes(msg);
socket.flush();
} else {
trace("Cannot write to server because there is no connection!");
}
}
我们可以看到,这段代码里,我注释掉了socket.writeMultiByte(msg,"gb2312");此句代码。从字面意思上也可以了解,这里是使用了"gb2312"的编码来发送消息。
flash socket提供了丰富的方法,在以后的教程里,我会详细描述flash.net.Socket的每一个方法。
获取消息
如果服务器发送消息给客户端,我们也可注册一个回调来获取消息的内容
socket.addEventListener(flash.events.ProgressEvent.SOCKET_DATA, function(e) : Void {
//var msg = socket.readMultiByte(socket.bytesAvailable,"gb2312");
var msg = socket.readUTFBytes(socket.bytesAvailable);
trace("Received : " + msg );
flash.external.ExternalInterface.call("setTimeout", jsScope + "receive('" + msg + "')", 0);
}
);
这里和发送消息类似,我们可以使用不同的编码标准来解码我们获得的字节流。
关闭连接
在完成通讯以后,如果我们需要关闭这个socket连接的话,可以使用下面的主动方法。
static function close() {
if (socket.connected) {
trace("Closing current connection");
socket.close();
} else {
trace("Cannot disconnect to server because there is no connection!");
}
}
连接断开
在socket连接断开以后,我们可以注册一个回调来获得通知
socket.addEventListener(flash.events.Event.CLOSE, function(e) : Void {
trace("Disconnected from server");
flash.external.ExternalInterface.call("setTimeout", jsScope + "disconnected()", 0);
}
);
IO错误
在通信过程中,如果有错误发生的话,我们可以这样来获得通知
socket.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e) : Void {
trace("IOERROR : " + e.text);
flash.external.ExternalInterface.call("setTimeout", jsScope + "ioError('" + e.text + "')" ,0);
}
);
完整的代码
//SocketBridge.hx
class SocketBridge {
static var socket = new flash.net.Socket();
static var jsScope;
static function main() {
if (flash.external.ExternalInterface.available) {
jsScope = flash.Lib.current.loaderInfo.parameters.scope;
if (jsScope == null) {
jsScope = "";
} else {
jsScope += ".";
}
/* Calls the javascript load method once the SWF has loaded */
flash.external.ExternalInterface.call("setTimeout", jsScope + "loaded()");
// Set event listeners for socket
// CONNECT
socket.addEventListener(flash.events.Event.CONNECT, function(e) : Void {
trace("Connected to server");
flash.external.ExternalInterface.call("setTimeout", jsScope + "connected()", 0);
}
);
// CLOSE
socket.addEventListener(flash.events.Event.CLOSE, function(e) : Void {
trace("Disconnected from server");
flash.external.ExternalInterface.call("setTimeout", jsScope + "disconnected()", 0);
}
);
// IO ERROR
socket.addEventListener(flash.events.IOErrorEvent.IO_ERROR, function(e) : Void {
trace("IOERROR : " + e.text);
flash.external.ExternalInterface.call("setTimeout", jsScope + "ioError('" + e.text + "')" ,0);
}
);
// SECURITY ERROR
socket.addEventListener(flash.events.SecurityErrorEvent.SECURITY_ERROR, function(e) : Void {
trace("SECURITY ERROR : " + e.text);
flash.external.ExternalInterface.call("setTimeout", jsScope + "securityError('" +e.text+ "')", 0);
}
);
// SOCKET DATA
socket.addEventListener(flash.events.ProgressEvent.SOCKET_DATA, function(e) : Void {
//var msg = socket.readMultiByte(socket.bytesAvailable,"gb2312");
var msg = socket.readUTFBytes(socket.bytesAvailable);
trace("Received : " + msg );
flash.external.ExternalInterface.call("setTimeout", jsScope + "receive('" + msg + "')", 0);
}
);
/* Set External Interface Callbacks */
// connect(host, port)
flash.external.ExternalInterface.addCallback("connect", connect);
// disconnect()
flash.external.ExternalInterface.addCallback("close", close);
// send()
flash.external.ExternalInterface.addCallback("send", send);
// log()
flash.external.ExternalInterface.addCallback("log", log);
} else {
trace("Flash external interface not available");
}
}
/**
* Connect to new socket server
* @param host The host the socket server resides on
* @param port The socket servers port
*/
static function connect(host : String, port : String) {
trace("Connecting to socket server at " + host + ":" + port);
socket.connect(host, Std.parseInt(port));
}
/**
* Disconnect the socket
*/
static function close() {
if (socket.connected) {
trace("Closing current connection");
socket.close();
} else {
trace("Cannot disconnect to server because there is no connection!");
}
}
/**
* Write string to the socket
*/
static function send(msg) {
if (socket.connected) {
trace("Writing '" + msg + "' to server");
//socket.writeMultiByte(msg,"gb2312");
socket.writeUTFBytes(msg);
socket.flush();
} else {
trace("Cannot write to server because there is no connection!");
}
}
/**
* Log
*/
static function log(msg) {
trace("log : "+msg);
}
}
编译
我使用了开源的haxe来编译,你也可以通过http://haxe.org/file/hxinst-win.exe获取它。
编写完SocketBridge.hx以后,你需要在同一个目录下建一个compile.hxml文本文件,它的内容是:
-swf socket_bridge.swf
-swf-version 9
-main SocketBridge
在安装过haxe的情况下,只要你双击执行compile.hxml,便可以在同一目录下获得编译出的socket_bridge.swf 。
在下一篇教程"客户端-javascript和flash互操作"里,我们将了解如何使用socket_bridge.swf来操作javascript进行socket/tcp通讯。
=============================
在上一篇"基础-web即时通讯系统的四种实现"中,我们讨论了如何使用flash/actionscript,来实现socket/tcp,并使用开源的haxe来编译。而本文将介绍如何使用javascript调用编译出的flash来进行通讯。
加载flash
我使用swfobject.js来加载我们编译出的flash文件——socket_bridge.swf。
swfobject.js是一种易用的、符合标准的在网页中嵌入flash对象的方法,你只需要在网页中引用一个小巧的js文件。你在网上可以很容易下载到它。
初始化flash
我们可以调用swfobject.js里提供的方法,来编写跨浏览器的加载flash的代码。
function initialFlash(){
var so = new SWFObject("socket_bridge.swf", "socketBridge", "800", "300", "9", "#ffffff");
so.addParam("allowscriptaccess", "always");
so.addVariable("scope", "socket");
so.write("flashcontainer");
}
这段代码的意义是,将socket_bridge.swf下载到浏览器端,并在id为flashcontainer的html元素下。并将对象名为socket的对象暴露给flash。
考虑到这里使用到了flashcontainer这个html元素,我们需要在这个html元素已经存在于dom以后才可以执行这段代码,比如在window.onload事件里。
onload=function(){
initialFlash();
}
封装flash socket
我们需要在initialFlash执行前声明一个对象名为socket的对象。
var socket = {
config :{
ip:"127.0.0.1",
port:4502,
flashcontainer:"flashcontainer",
auto:true
},
connect : function() {
socket.flash.log("begin connect to session server");
socket.flash.connect(socket.config.ip, socket.config.port);
},
send : function(msg) {
if(socket.isConnected >= 1){
socket.flash.send(msg);
}
},
loaded : function() {
socket.flash = document.getElementById("socketBridge");
socket.isConnected = 0;
if(socket.config.auto){
socket.connect();
}
},
connected : function() {
socket.isConnected = 1;
socket.flash.log("connected to session server");
},
close : function() {
socket.flash.close();
socket.isConnected = 0;
socket.flash.log("close connection");
},
disconnected : function() {
socket.isConnected = 0;
socket.flash.log("disconnected");
},
ioError: function(msg) {
socket.flash.log(msg);
socket.isConnected = 0;
},
securityError: function(msg) {
socket.flash.log(msg);
socket.isConnected = 0;
},
receive: function(msg) {
//callback
}
};
这里有几处需要注意:
socket.config.auto为true时,flash文件socket_bridge.swf加载完毕时,自动调用socket.flash.connect连接本地的4502端口。
socket.flash.log为对flash的trace函数的封装,可以调用它向flash控制台写入一些调试信息。
socket.isConnected为socket连接的状态量,实际上在真实的聊天室里,连接完以后还需要登录和验证的一系列过程,所以用此变量来表示这些自定义状态。
操作flash socket
有了这个socket对象,我们便可以在javascript里操作flash socket了。比如说:
socket.send("hello socket")
效果演示
完整代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="js/swfobject.js"></script>
<script>
var socket = {
config :{
ip:"127.0.0.1",
port:4502,
flashcontainer:"flashcontainer",
auto:true
},
connect : function() {
socket.flash.log("begin connect to session server");
socket.flash.connect(socket.config.ip, socket.config.port);
},
send : function(msg) {
if(socket.isConnected >= 1){
socket.flash.send(msg);
}
},
loaded : function() {
socket.flash = document.getElementById("socketBridge");
socket.isConnected = 0;
if(socket.config.auto){
socket.connect();
}
},
connected : function() {
socket.isConnected = 1;
socket.flash.log("connected to session server");
},
close : function() {
socket.flash.close();
socket.isConnected = 0;
socket.flash.log("close connection");
},
disconnected : function() {
socket.isConnected = 0;
socket.flash.log("disconnected");
},
ioError: function(msg) {
socket.flash.log(msg);
socket.isConnected = 0;
},
securityError: function(msg) {
socket.flash.log(msg);
socket.isConnected = 0;
},
receive: function(msg) {
//callback
}
};
function initialFlash(){
var so = new SWFObject("socket_bridge.swf", "socketBridge", "800", "300", "9", "#ffffff");
so.addParam("allowscriptaccess", "always");
so.addVariable("scope", "socket");
so.write(socket.config.flashcontainer);
}
onload=function(){
initialFlash();
}
</script>
</head>
<body>
<textarea id="cmd" style="width: 400px; height: 300px;">socket.send("")</textarea>
<input type="button" value="exec" onclick="eval(document.getElementById('cmd').value);" />
<div id="flashcontainer">
</div>
</body>
</html>
===============
简介
policy-file是flash socket安全机制的重要部分,而本文详细介绍提供policy-file的policyserver的实现过程。事实上,policyserver本身也是一个socket服务器端的简单原型。了解本文也将为教程中后续的sessionserver的讨论有所铺垫。
什么是policy-file
policy-file是一个flash的安全控制机制,它的设置决定了是否允许flash程序进行跨域通讯。这是对ajax,dom无法跨域的一个拓展。
一个典型的policy-file可能是这样的:
<?xml version="1.0" encoding="UTF-8"?>
<cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd">
<allow-access-from domain="*" to-ports="*" secure="false" />
<site-control permitted-cross-domain-policies="master-only" />
</cross-domain-policy>
其中你可以在policy-file指定允许开放的域名/ip,或者是端口。
policy-file的请求过程
1 flash socket在真正执行socket.connect,以连接到指定的ip和端口前,会先尝试连接该ip的843端口
2如果843端口被监听,并且在连接上后接收到一个policy-file.xml的话,才会断开与843端口的连接
3 flash会分析policy-file的内容,并确认是否访问指定的ip和端口是否是被允许的
4 如果指定的ip和端口是否是被允许的,flash才会真正的连接到指定的ip和端口
policy-file的请求过程的异常情况
1 如果843端口连接失败或者超时,flash依然会连接指定端口,并尝试从指定端口获取policy-file
2如果policy-file获取失败,或者指定的ip和端口不在policy-file允许范围内,flash socket将抛出flash.events.SecurityErrorEvent.SECURITY_ERROR这个异常
policyserver的实现
监听端口
IPAddress ipAddress = IPAddress.Parse(ip);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 843);
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
allDone.Reset();
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
其中allDone是一个信号控制变量。在接收到一个新的连接请求前,policyserver会阻塞在allDone.WaitOne()这一句。
处理接入请求
public static void AcceptCallback(IAsyncResult ar)
{
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
Console.WriteLine("conneceted : " + handler.RemoteEndPoint.ToString());
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
在上面的代码里,我还把客户端的ip写入控制台,并将异步接受flash的第一个请求。
读入请求,写回policy-file
当接入一个连接请求以后,allDone.Set()释放信号量,在等待接入的循环体里,将完成一次过程,进入下一次循环。
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead));
content = state.sb.ToString();
if ("<policy-file-request/>\0" == content)
{
Send(handler,
@"
<?xml version=""1.0"" encoding=""UTF-8""?>
<cross-domain-policy xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">
<allow-access-from domain=""*"" to-ports=""*"" secure=""false"" />
<site-control permitted-cross-domain-policies=""master-only"" />
</cross-domain-policy>
"
);
}
else
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
policyserver确认第一个请求,是否是"<policy-file-request/>\0",如果是,则将策略文件异步写回给flash。
关闭socket
private static void SendCallback(IAsyncResult ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
int bytesSent = handler.EndSend(ar);
Console.WriteLine("policy file sent");
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
写完策略文件后,在控制台写入作为提示信息。并关闭socket。
配置
考虑到这个policyserver可能部署在不同的环境,为了方便部署,我从文件名中读取配置。
Match m = Regex.Match(Environment.CommandLine, @"\d+\.\d+\.\d+\.\d+");
string ip = string.IsNullOrEmpty(m.Value) ? "127.0.0.1" : m.Value;
Console.WriteLine("binding to " + ip);
比如编译结果文件为SimplePolicyServer.exe,我把它文件名修改为SimplePolicyServer.192.168.1.10.exe ,那么它在执行的时候,便会监听192.168.1.10这个ip的端口。如果没有指定正确的ip的话,默认绑定到本机,即127.0.0.1。
完整代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Net.NetworkInformation;
using System.Text.RegularExpressions;
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
namespace SimplePolicyServer
{
class Program
{
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public static void StartListening()
{
byte[] bytes = new Byte[1024];
Match m = Regex.Match(Environment.CommandLine, @"\d+\.\d+\.\d+\.\d+");
string ip = string.IsNullOrEmpty(m.Value) ? "127.0.0.1" : m.Value;
Console.WriteLine("binding to " + ip);
IPAddress ipAddress = IPAddress.Parse(ip);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 843);
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
allDone.Reset();
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.Read();
}
public static void AcceptCallback(IAsyncResult ar)
{
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
Console.WriteLine("conneceted : " + handler.RemoteEndPoint.ToString());
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
state.sb.Append(Encoding.UTF8.GetString(state.buffer, 0, bytesRead));
content = state.sb.ToString();
if ("<policy-file-request/>\0" == content)
{
Send(handler,
@"
<?xml version=""1.0"" encoding=""UTF-8""?>
<cross-domain-policy xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:noNamespaceSchemaLocation=""http://www.adobe.com/xml/schemas/PolicyFileSocket.xsd"">
<allow-access-from domain=""*"" to-ports=""*"" secure=""false"" />
<site-control permitted-cross-domain-policies=""master-only"" />
</cross-domain-policy>
"
);
}
else
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
private static void Send(Socket handler, String data)
{
byte[] byteData = Encoding.ASCII.GetBytes(data);
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
int bytesSent = handler.EndSend(ar);
Console.WriteLine("policy file sent");
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args)
{
StartListening();
return 0;
}
}
}