稳扎稳打Silverlight(24) - 2.0通信之Socket, 开发一个多人聊天室

稳扎稳打Silverlight(24) - 2.0通信之Socket, 开发一个多人聊天室,第1张

概述  [索引页] [源码下载] 稳扎稳打Silverlight(24) - 2.0通信之Socket, 开发一个多人聊天室 作者: webabcd 介绍 Silverlight 2.0 Socket通信。开发一个多人聊天室     服务端:实例化Socket, 绑定, 监听, 连接, 接收数据, 发送数据     客户端:实例化Socket, 指定服务端地址, 连接, 接收数据, 发送数据 在线DE   [索引页]
[源码下载]

稳扎稳打Silverlight(24) - 2.0通信之Socket,开发一个多人聊天室
作者: webabcd
介绍
Silverlight 2.0 Socket通信。开发一个多人聊天室
    服务端:实例化Socket,绑定,监听,连接,接收数据,发送数据
    客户端:实例化Socket,指定服务端地址,255); orphans:2; wIDows:2">在线DEMO
http://www.cnblogs.com/webabcd/archive/2008/10/09/1307486.html  
示例
1、Policy服务(向客户端发送策略文件的服务)
clIEntaccesspolicy.xml

<? xml version="1.0" enCoding ="utf-8" ?>

< access-policy >

    
< cross-domain-access >

        
< policy >

            
< allow-from >

                
< domain  uri ="*"   />

            
</ allow-from >

            
< grant-to >

                
< socket-resource  port ="4502-4534"  protocol ="tcp"   />

            
</ grant-to >

        
</ policy >

    
</ cross-domain-access >

</ access-policy >
Main.cs

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.sockets;

using  System.IO;

using  System.Net;


namespace  PolicyServer

{

    
public partial class Main : Form

    
{

        
// 客户端 socket 发送到服务端的对策略文件的请求信息

        private Readonly string _policyRequestString = "<policy-file-request/>";


        
private Socket _Listener; // 服务端监听的 socket

        private byte[] _policyBuffer; // 服务端策略文件的 buffer

        private byte[] _requestBuffer; // 客户端 socket 发送的请求信息的 buffer


        
private int _received; // 接收到的信息字节数


        
private bool _flag = false// 标志位。服务端是否要处理传入的连接


        System.Threading.SynchronizationContext _syncContext;


        
public Main()

        
{

            InitializeComponent();


            _flag 
= true;


            lblStatus.Text 
= "PolicyServer状态:启动";

            lblStatus.Forecolor 
= color.Green;


            
// 启动 PolicyServer

            StartupPolicyServer();


            
// UI 线程

            _syncContext = System.Threading.SynchronizationContext.Current;

        }


        
private voID btnStartup_Click(object sender, EventArgs e)

        
{

            _flag 
= true;


            lblStatus.Text 
= "PolicyServer状态:启动";

            lblStatus.Forecolor 
= color.Green;

        }


        
private voID btnPause_Click(object sender, EventArgs e)

        
{

            _flag 
= false;


            lblStatus.Text 
= "PolicyServer状态:暂停";

            lblStatus.Forecolor 
= color.Red;

        }


        
/// <summary>

        
/// 启动 PolicyServer

        
/// </summary>

        private voID StartupPolicyServer()

        
{

            
string policyfile = Path.Combine(Application.StartupPath, "clIEntaccesspolicy.xml");


            
using (fileStream fs = new fileStream(policyfile, fileMode.Open, fileAccess.Read))

            
{

                
// 将策略文件的内容写入 buffer

                _policyBuffer = new byte[fs.Length];

                fs.Read(_policyBuffer, 
0, _policyBuffer.Length);

            }


            
// 初始化 socket , 然后与端口绑定, 然后对端口进行监听

            _Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            _Listener.Bind(
new IPEndPoint(IPAddress.Any, 943)); // socket 请求策略文件使用 943 端口

            _Listener.Listen(100);


            
// 开始接受客户端传入的连接

            _Listener.BeginAccept(new AsyncCallback(OnClIEntConnect), null);

        }


        
private voID OnClIEntConnect(IAsyncResult result)

        
{

            
if (!_flag)

            
{

                
// PolicyServer 停用的话,则不再处理传入的连接

                _Listener.BeginAccept(new AsyncCallback(OnClIEntConnect), null);

                
return;

            }

                 

            Socket clIEnt; 
// 客户端发过来的 socket


            
try

            
{

                
// 完成接受客户端传入的连接的这个异步 *** 作,并返回客户端连入的 socket

                clIEnt = _Listener.EndAccept(result);

            }

            
catch (SocketException)

            
{

                
return;

            }


            _requestBuffer 
= new byte[_policyRequestString.Length];

            _received 
= 0;


            
try

            
{

                
// 开始接收客户端传入的数据

                clIEnt.BeginReceive(_requestBuffer, _policyRequestString.Length, SocketFlags.None, new AsyncCallback(OnReceive), clIEnt);

            }

            
catch (SocketException)

            
{

                
// socket 出错则关闭客户端 socket

                clIEnt.Close();

            }


            
// 继续开始接受客户端传入的连接

            _Listener.BeginAccept(new AsyncCallback(OnClIEntConnect), null);

        }



        
private voID OnReceive(IAsyncResult result)

        
{

            Socket clIEnt 
= result.AsyncState as Socket;


            
try

            
{

                
// 完成接收数据的这个异步 *** 作,并计算累计接收的数据的字节数

                _received += clIEnt.EndReceive(result);


                
if (_received < _policyRequestString.Length)

                
{

                    
// 没有接收到完整的数据,则继续开始接收

                    clIEnt.BeginReceive(_requestBuffer, _received, _policyRequestString.Length - _received, clIEnt);

                    
return;

                }


                
// 把接收到的数据转换为字符串

                string request = System.Text.EnCoding.UTF8.GetString(_requestBuffer, _received);


                
if (StringComparer.InvariantCultureIgnoreCase.Compare(request, _policyRequestString) != 0)

                
{

                    
// 如果接收到的数据不是“<policy-file-request/>”,则关闭客户端 socket

                    clIEnt.Close();

                    
return;

                }


                
// 开始向客户端发送策略文件的内容

                clIEnt.BeginSend(_policyBuffer, _policyBuffer.Length, new AsyncCallback(OnSend), clIEnt);

            }


            
catch (SocketException)

            
{

                
// socket 出错则关闭客户端 socket

                clIEnt.Close();

            }

        }


        
private voID OnSend(IAsyncResult result)

        
{

            Socket clIEnt 
= result.AsyncState as Socket;


            
try

            
{

                
// 完成将信息发送到客户端的这个异步 *** 作

                clIEnt.EndSend(result);


                
// 获取客户端的ip地址及端口号,并显示

                _syncContext.Post(ResultCallback, clIEnt.LocalEndPoint.ToString());

            }

            
finally

            
{

                
// 关闭客户端 socket

                clIEnt.Close();

            }

        }


        
voID ResultCallback(object result)

        
{

            
// 输出客户端的ip地址及端口号

            txtMsg.Text += result.ToString() + "\r\n";

        }

    }

}


2、Socket服务端(聊天室的服务端)
ClIEntSocketPacket.cs

using  System;

using  System.Collections.Generic;

using  System.linq;

using  System.Text;


namespace  SocketServer

{

    
/// <summary>

    
/// 对客户端 Socket 及其他相关信息做一个封装

    
/// </summary>

    public class ClIEntSocketPacket

    
{

        
/// <summary>

        
/// 客户端 Socket

        
/// </summary>

        public System.Net.sockets.socket Socket getset; }


        
private byte[] _buffer;

        
/// <summary>

        
/// 为该客户端 Socket 开辟的缓冲区

        
/// </summary>

        public byte[] Buffer

        
{

            
get

            
{

                
if (_buffer == null)

                    _buffer 
= new byte[32];


                
return _buffer;

            }

        }


        
private List<byte> _receivedByte;

        
/// <summary>

        
/// 客户端 Socket 发过来的信息的字节集合

        
/// </summary>

        public List<byte> ReceivedByte

        
{

            
get

            
{

                
if (_receivedByte == null)

                    _receivedByte 
= new List<byte>();


                
return _receivedByte;

            }

        }

    }

}


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.sockets;

using  System.Net;

using  System.Threading;

using  System.IO;


namespace  SocketServer

{

    
public partial class Main : Form

    
{

        SynchronizationContext _syncContext;


        System.Timers.Timer _timer;


        
// 信息结束符,用于判断是否完整地读取了用户发送的信息(要与客户端的信息结束符相对应)

        private string _endMarker = "^";


        
// 服务端监听的 socket

        private Socket _Listener;


        
// 实例化 ManualresetEvent, 设置其初始状态为非终止状态(可入状态)

        private ManualresetEvent _connectDone = new ManualresetEvent(false);


        
// 客户端 Socket 列表

        private List<ClIEntSocketPacket> _clIEntList = new List<ClIEntSocketPacket>();


        
public Main()

        
{

            InitializeComponent();


            
// UI 线程

            _syncContext = SynchronizationContext.Current;


            
// 启动后台线程去运行 Socket 服务

            Thread thread = new Thread(new ThreadStart(StartupSocketServer));

            thread.IsBackground 
= true;

            thread.Start();

        }


        
private voID StartupSocketServer()

        
{

            
// 每 10 秒运行一次计时器所指定的方法

            _timer = new System.Timers.Timer();

            _timer.Interval 
= 10000d;

            _timer.Elapsed 
+= new System.Timers.ElapsedEventHandler(_timer_Elapsed);

            _timer.Start();


            
// 初始化 socket , 然后与端口绑定, 然后对端口进行监听

            _Listener = new Socket(AddressFamily.InterNetwork, 4518)); // Silverlight 2.0 使用 Socket 只能连接 4502-4534 端口

            _Listener.Listen(100);



            
while (true)

            
{

                
// 重置 ManualresetEvent,由此线程来控制 ManualresetEvent,其它到这里来的线程请等待

                
// 为求简单易懂,本例实际上只有主线程会在这里循环运行

                _connectDone.reset();


                
// 开始接受客户端传入的连接

                _Listener.BeginAccept(new AsyncCallback(OnClIEntConnect), null);


                
// 阻止当前线程,直到当前 ManualresetEvent 调用 Set 发出继续信号

                _connectDone.WaitOne();

            }

        }


        
private voID _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)

        
{

            
// 每 10 秒给所有连入的客户端发送一次消息

            SendData(string.Format("webabcd 对所有人说:大家好! 【信息来自服务端 {0}】", DateTime.Now.ToString("hh:mm:ss")));

        }


        
private voID OnClIEntConnect(IAsyncResult async)

        
{

            
// 当前 ManualresetEvent 调用 Set 以发出继续信号,从而允许继续执行一个或多个等待线程

            _connectDone.Set();


            ClIEntSocketPacket clIEnt 
= new ClIEntSocketPacket();

            
// 完成接受客户端传入的连接的这个异步 *** 作,并返回客户端连入的 socket

            clIEnt.socket = _Listener.EndAccept(async);


            
// 将客户端连入的 Socket 放进客户端 Socket 列表

            _clIEntList.Add(clIEnt);



            SendData(
"一个新的客户端已经成功连入服务器。。。 【信息来自服务端】");



            
try

            
{

                
// 开始接收客户端传入的数据

                clIEnt.socket.BeginReceive(clIEnt.Buffer, clIEnt.Buffer.Length, new AsyncCallback(OnDataReceived), clIEnt);

            }

            
catch (SocketException ex)

            
{

                
// 处理异常

                HandleException(clIEnt, ex);

            }

        }


        
private voID OnDataReceived(IAsyncResult async)

        
{

            ClIEntSocketPacket clIEnt 
= async.AsyncState as ClIEntSocketPacket;


            
int count = 0;


            
try

            
{

                
// 完成接收数据的这个异步 *** 作,并返回接收的字节数

                if (clIEnt.socket.Connected)

                    count 
= clIEnt.socket.EndReceive(async);

            }

            
catch (SocketException ex)

            
{

                HandleException(clIEnt, ex);

            }


            
// 把接收到的数据添加进收到的字节集合内

            
// 本例采用UTF8编码,中文占用3字节,英文占用1字节,缓冲区为32字节

            
// 所以如果直接把当前缓冲区转成字符串的话可能会出现乱码,所以要等接收完用户发送的全部信息后再转成字符串

            foreach (byte b in clIEnt.Buffer.Take(count))

            
{

                
if (b == 0continue// 如果是空字节则不做处理


                clIEnt.ReceivedByte.Add(b);

            }


            
// 把当前接收到的数据转换为字符串。用于判断是否包含自定义的结束符

            string receivedString = UTF8EnCoding.UTF8.GetString(clIEnt.Buffer, count);


            
// 如果该 Socket 在网络缓冲区中没有排队的数据 并且 接收到的数据中有自定义的结束符时

            if (clIEnt.socket.Connected && clIEnt.socket.Available == 0 && receivedString.Contains(_endMarker))

            
{

                
// 把收到的字节集合转换成字符串(去掉自定义结束符)

                
// 然后清除掉字节集合中的内容,以准备接收用户发送的下一条信息

                string content = UTF8EnCoding.UTF8.GetString(clIEnt.ReceivedByte.ToArray());

                content 
= content.Replace(_endMarker, "");

                clIEnt.ReceivedByte.Clear();


                
// 发送数据到所有连入的客户端,并在服务端做记录

                SendData(content);

                _syncContext.Post(ResultCallback, content);

            }


            
try

            
{

                
// 继续开始接收客户端传入的数据

                if (clIEnt.socket.Connected)

                    clIEnt.socket.BeginReceive(clIEnt.Buffer, clIEnt);

            }

            
catch (SocketException ex)

            
{

                HandleException(clIEnt, ex);

            }

        }


        
/// <summary>

        
/// 发送数据到所有连入的客户端

        
/// </summary>

        
/// <param name="data">需要发送的数据</param>

        private voID SendData(string data)

        
{

            
byte[] byteData = UTF8EnCoding.UTF8.GetBytes(data);


            
foreach (ClIEntSocketPacket clIEnt in _clIEntList)

            
{

                
if (clIEnt.socket.Connected)

                
{

                    
try

                    
{

                        
// 如果某客户端 Socket 是连接状态,则向其发送数据

                        clIEnt.socket.BeginSend(byteData, byteData.Length, new AsyncCallback(OnDataSent), clIEnt);

                    }

                    
catch (SocketException ex)

                    
{

                        HandleException(clIEnt, ex);

                    }

                }

                
else 

                
{

                    
// 某 Socket 断开了连接的话则将其关闭,并将其清除出客户端 Socket 列表

                    
// 也就是说每次向所有客户端发送消息的时候,都会从客户端 Socket 集合中清除掉已经关闭了连接的 Socket

                    clIEnt.socket.Close();

                    _clIEntList.Remove(clIEnt);

                }

            }

        }


        
private voID OnDataSent(IAsyncResult async)

        
{

            ClIEntSocketPacket clIEnt 
= async.AsyncState as ClIEntSocketPacket;


            
try

@H_281_4041@

            
{

                
// 完成将信息发送到客户端的这个异步 *** 作

                if (clIEnt.socket.Connected)

                    clIEnt.socket.EndSend(async);

            }

            
catch (SocketException ex)

            
{

                HandleException(clIEnt, ex);

            }

        }


        
/// <summary>

        
/// 处理 SocketException 异常

        
/// </summary>

        
/// <param name="clIEnt">导致异常的 ClIEntSocketPacket</param>

        
/// <param name="ex">SocketException</param>

        private voID HandleException(ClIEntSocketPacket clIEnt, SocketException ex)

@H_9_4194@

        
{

            
// 在服务端记录异常信息,关闭导致异常的 Socket,并将其清除出客户端 Socket 列表

            _syncContext.Post(ResultCallback, clIEnt.socket.RemoteEndPoint.ToString() + " - " + ex.Message);

            clIEnt.socket.Close();

            _clIEntList.Remove(clIEnt);

        }


        
private voID ResultCallback(object result)

        
{

            
// 输出相关信息

            txtMsg.Text += result.ToString() + "\r\n";

        }

    }

}

3、Socket客户端(聊天室的客户端)
SocketClIEnt.xaml

< UserControl  x:Class ="Silverlight20.Communication.socketClIEnt"

    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  

    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" >

    
< StackPanel  HorizontalAlignment ="left"  WIDth ="600"  margin ="5"  Background ="Gray" >


        
< ScrollVIEwer  x:name ="scrollChat"  Height ="400"  VerticalScrollbarVisibility ="auto"  Background ="White"  margin ="10" >

            
< TextBlock  x:name ="txtChat"  textwrapPing ="Wrap"   />

        
</ ScrollVIEwer >


        
< StackPanel  OrIEntation ="Horizontal"  margin ="5" >

            
< TextBox  x:name ="txtname"  margin ="5"  WIDth ="100"   />

            
< TextBox  x:name ="txtinput"  margin ="5"  WIDth ="400"  KeyDown ="txtinput_KeyDown"   />

            
< button  x:name ="btnSend"  margin ="5"  WIDth ="60"  Content ="Send"  Click ="btnSend_Click" />

        
</ StackPanel >


    
</ StackPanel >

</ UserControl >


SocketClIEnt.xaml.cs

using  System;

using  System.Collections.Generic;

using  System.linq;

using  System.Net;

using  System.windows;

using  System.windows.Controls;

using  System.windows.documents;

using  System.windows.input;

using  System.windows.Media;

using  System.windows.Media.Animation;

using  System.windows.Shapes;


using  System.Net.sockets;

using  System.Text;


namespace  Silverlight20.Communication

{

    
public partial class SocketClIEnt : UserControl

    
{

        
// 信息结束符,用于判断是否完整地读取了用户发送的信息(要与服务端的信息结束符相对应)

        private string _endMarker = "^";


        
// 客户端 Socket

        private Socket _socket;


        
// Socket 异步 *** 作对象

        private socketasynceventargs _sendEventArgs;


        
public SocketClIEnt()

        
{

            InitializeComponent();


            
this.Loaded += new RoutedEventHandler(Page_Loaded);

        }


        
voID Page_Loaded(object sender, RoutedEventArgs e)

        
{

            
// 初始化姓名和需要发送的默认文字

            txtname.Text = "匿名用户" + new Random().Next(09999).ToString().Padleft(4'0');

            txtinput.Text 
= "hi";


            
// 实例化 Socket

            _socket = new Socket(AddressFamily.InterNetwork, ProtocolType.Tcp);


            
// 实例化 socketasynceventargs ,用于对 Socket 做异步 *** 作,很方便

            socketasynceventargs args = new socketasynceventargs();

            
// 服务器的 EndPoint

            args.RemoteEndPoint = new DnsEndPoint("wanglei-pc"4518);

            
// 异步 *** 作完成后执行的事件

            args.Completed += new EventHandler<socketasynceventargs>(OnSocketConnectCompleted);


            
// 异步连接服务端

            _socket.ConnectAsync(args);

        }


        
private voID OnSocketConnectCompleted(object sender, socketasynceventargs e)

        
{

            
// 设置数据缓冲区

            byte[] response = new byte[1024];

            e.SetBuffer(response, response.Length);


            
// 修改 socketasynceventargs 对象的异步 *** 作完成后需要执行的事件

            e.Completed -= new EventHandler<socketasynceventargs>(OnSocketConnectCompleted);

            e.Completed 
+= new EventHandler<socketasynceventargs>(OnSocketReceiveCompleted);


            
// 异步地从服务端 Socket 接收数据

            _socket.ReceiveAsync(e);


            
// 构造一个 socketasynceventargs 对象,用于用户向服务端发送消息

            _sendEventArgs = new socketasynceventargs();

            _sendEventArgs.RemoteEndPoint 
= e.RemoteEndPoint;


            
string data = "";

            
if (!_socket.Connected)

                data 
= "无法连接到服务器。。。请刷新后再试。。。";

            
else

                data 
= "成功地连接上了服务器。。。";


            WriteText(data);

        }


        
private voID OnSocketReceiveCompleted(object sender, socketasynceventargs e)

        
{

            
try

            
{

                
// 将接收到的数据转换为字符串

                string data = UTF8EnCoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);


                WriteText(data);

            }

            
catch (Exception ex)

            
{

                WriteText(ex.ToString());

            }


            
// 继续异步地从服务端 Socket 接收数据

            _socket.ReceiveAsync(e);

        }


        
private voID WriteText(string data)

        
{

            
// 在聊天文本框中输出指定的信息,并将滚动条滚到底部

            this.dispatcher.BeginInvoke(

                
delegate

                
{

                    txtChat.Text 
+= data + "\r\n";

                    scrollChat.ScrollToVerticalOffset(txtChat.ActualHeight);

                }

            );

        }


        
private voID SendData()

        
{

            
if (_socket.Connected)

            
{

                
// 设置需要发送的数据的缓冲区

                _sendEventArgs.BufferList =

                    
new List<ArraySegment<byte>>() 

                    

                        
new ArraySegment<byte>(UTF8EnCoding.UTF8.GetBytes(txtname.Text + "" + txtinput.Text + _endMarker)) 

                    }
;


                
// 异步地向服务端 Socket 发送消息

                _socket.SendAsync(_sendEventArgs);

            }

            
else

            
{

                txtChat.Text 
+= "无法连接到服务器。。。请刷新后再试。。。\r\n";

                _socket.Close();

            }


            txtinput.Focus();

            txtinput.Text 
= "";

        }


        
private voID btnSend_Click(object sender, RoutedEventArgs e)

        
{

            SendData();

        }


        
private voID txtinput_KeyDown(object sender, KeyEventArgs e)

        
{

            
// 按了回车键就向服务端发送数据

            if (e.Key == Key.Enter)

                SendData();

        }

    }

}


OK
[源码下载] 总结

以上是内存溢出为你收集整理的稳扎稳打Silverlight(24) - 2.0通信之Socket, 开发一个多人聊天室全部内容,希望文章能够帮你解决稳扎稳打Silverlight(24) - 2.0通信之Socket, 开发一个多人聊天室所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1071689.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存