【转】flex与c#基于socket的实时互动网络游戏编程教程二 .
这节讲讲如何用flex与c#进行socket通讯。
Flex端(当然你也完全可以用flash来写)使用import flash.net.Socket;包来做。通过ProgressEvent.SOCKET_DATA时刻检听是否接收到数据,接收到数据后触发函数receiveData。
使用var message:String=CurSocket.readMultiByte(CurSocket.bytesAvailable,"GB2312");可以把c#服务器发送过来的数据接收到。
CurSocket.writeUTF(Message.text);可以将数据发送给指定c#服务器
至于服务器端,只需要将第一节讲的代码稍加修改就可以使用。
需要注意的是这里要声明一个User类,其实本例中不使用类,直接在代码里声明TcpClient client也是可以的,做类的原因是在以后做游戏的时候,要给每一个连接进来的客户附加一些其他信息,比如用户名,血量,速度之类的,所以这里就先做成类的模式。
C#的代码很简单,等待连接,有客户连接后,把客户对象转换成自定义类User并保存到一个数组中。
当任何客户发送消息后,用循环向所有数组中的用户发送消息即可。下面请看代码。
Flex代码:
<?xml version="1.0" encoding="gb2312"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
initialize="connectToServer();">
<mx:Script>
<![CDATA[
import flash.net.*;
import flash.net.Socket;
import flash.system.Security;
import mx.controls.Alert;
private var CurSocket:Socket=new Socket();
private function connectToServer() : void
{
CurSocket.addEventListener(Event.CLOSE,close);
CurSocket.addEventListener(ProgressEvent.SOCKET_DATA,receiveData);
CurSocket.addEventListener(IOErrorEvent.IO_ERROR,ioErrorHandler);
CurSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR,securityHandler);
CurSocket.connect("192.168.1.100",8888); //本机测试不要用127.0.0.1,否则连不上,不知原因
}
private function close(event: Event) : void
{
Alert.show("失去与服务器的连接!");
}
private function receiveData(event:ProgressEvent) : void
{
//trace("socketDataHandler: " + event);
var message:String=CurSocket.readMultiByte(CurSocket.bytesAvailable,"GB2312");
trace(message);
if(message != "")
{
MessageBoard.text += message + "/n";
}
/*
try
{
var buffer : ByteArray = new ByteArray();
CurSocket.readBytes(buffer,0,CurSocket.bytesAvailable);
var message : String = buffer.readUTF();
if(message != "")
{
MessageBoard.text += message + "/n";
}
}catch(ex : Error){}
*/
}
private function ioErrorHandler(event: IOErrorEvent) : void
{
Alert.show("IO_ERROR!");
}
private function securityHandler(event: SecurityErrorEvent) : void
{
Alert.show("SECURITY_ERROR!");
Alert.show(event.toString());
}
private function send() : void
{
if(CurSocket.connected == true)
{
if(Message.text == "")
{
Alert.show("请输入要发送的信息!");
return;
}
CurSocket.writeUTF(Message.text);
CurSocket.flush();
Message.text = "";
}
else
{
Alert.show("失去与服务器的连接!");
}
}
]]>
</mx:Script>
<mx:Button x="377.75" y="264" label="Send" click="send();"/>
<mx:TextInput id="Message" x="18.25" y="264" width="351"/>
<mx:TextArea id="MessageBoard" x="18.25" y="10" height="246" width="413.5"/>
</mx:Application>
C#代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
namespace flexSocketWinFrom
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
IPAddress localAddress;
private const int port = 8888;
private TcpListener myListener;
private List<TcpClient> userList = new List<TcpClient>();
public BinaryReader br;
private BinaryWriter bw;
private void Form1_Load(object sender, EventArgs e)
{
//IPAddress[] addrIP = Dns.GetHostAddresses(Dns.GetHostName());
//localAddress = addrIP[0];
localAddress = IPAddress.Parse("192.168.1.100");
}
private void button1_Click(object sender, EventArgs e)
{
myListener = new TcpListener(localAddress, port);
myListener.Start();
textBox1.Text = string.Format("开始在{0}:{1}监听客户连接", localAddress, port);
//创建一个线程监听客户端连接请求
Thread myThread = new Thread(ListenClientConnect);
myThread.Start();
}
private void ListenClientConnect()
{
TcpClient newClient = null;
while (true)
{
try
{
newClient = myListener.AcceptTcpClient();//当有客户连接时执行一次下面的步骤
}
catch
{
//当单击“停止监听”或者退出此窗体时AcceptTcpClient()会产生异常
//因此可以利用此异常退出循环
break;
}
//每接受一个客户端连接,就创建一个对应的线程循环接收该客户端发来的信息
User user = new User(newClient);
Thread threadReceive = new Thread(ReceiveData);
threadReceive.Start(user);
userList.Add(newClient);//这个列表保存了所有连接中的用户
textShow(string.Format("[{0}]进入", newClient.Client.RemoteEndPoint));
textShow(string.Format("当前连接用户数:{0}", userList.Count));
}
}
//现成援引From控件
delegate void SetTextCallback(string text);
private void textShow(string str)
{
if (textBox1.InvokeRequired)
{
SetTextCallback d = textShow;
textBox1.Invoke(d, str);
}
else
{
this.textBox1.Text = "/n" + textBox1.Text + str;
}
}
private void ReceiveData(object userState)
{
User user = (User)userState;
TcpClient client = user.client;
while (true)
{
string receiveString = null;
try
{
textShow(string.Format("#接收前#"));
//这里会阻塞,直到接收到客户端消息。值得注意的是这里每一个user都是独立的(应该是因为在类中创建client的原因),之前连接的所有客用端都会被监听,而不会被覆盖
//如果直接在这创建client,则后面连接的client会覆盖掉前面连接的client,导致最后最后一个终端可以发消息
receiveString = user.br.ReadString();
textShow(string.Format("#接收后#"));
}
catch
{
break;
}
textShow(string.Format("[{0}]被输出;", receiveString));
// bw.Write(System.Text.Encoding.GetEncoding("gb2312").GetBytes(receiveString));
// bw.Flush();
//如果想给所有客用端发信息,在开始时应该把客用端保存在一个array里,然后这里用循环发送
SendToAllClient(client, receiveString);
}
}
/// <summary>发送信息给所有客户</summary>
/// <param name="user">指定发给哪个用户</param>
/// <param name="message">信息内容</param>
private void SendToAllClient(TcpClient ClientUser, string message)
{
for (int i = 0; i < userList.Count; i++)
{
TcpClient client = (TcpClient)userList[i];
NetworkStream networkStream = client.GetStream();
//将网络流作为二进制读写对象
br = new BinaryReader(networkStream);
bw = new BinaryWriter(networkStream);
bw.Write(System.Text.Encoding.GetEncoding("gb2312").GetBytes(message));
bw.Flush();
}
}
}
}
User.cs类
using System.Net.Sockets;
using System.IO;
namespace flexSocketWinFrom
{
class User
{
public TcpClient client { get; private set; }
public BinaryReader br { get; private set; }
public BinaryWriter bw { get; private set; }
public string userName { get; set; }
public User(TcpClient client)
{
this.client = client;
NetworkStream networkStream = client.GetStream();
br = new BinaryReader(networkStream);
bw = new BinaryWriter(networkStream);
}
public void Close()
{
br.Close();
bw.Close();
client.Close();
}
}
}
本例中的重点就是通过代码readMultiByte可以从flex或flash向服务器发送socket消息。你打开2个页面就可以测试了,本例未解决的两个问题是一、客用关掉flex页面,服务器是不知道的。Flex本身也没有吸构函数,在后面的教程中将会讲解如何用心跳测试法及时获得离开用户。二、本例在本机运行是没有问题的,但是放到iis或者tomcat下会有安全权限错误,就是沙箱限制,这个显示网上都是写通过设置策略文件xml解决,其实不然,socket的沙箱限制和http的是不一样。这2个问题在以后的教程中将会解答。