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

简介:

 

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.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, SocketType.Stream, ProtocolType.Tcp); 
                        _listener.Bind( new IPEndPoint(IPAddress.Any, 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, 0, client.Buffer.Length, SocketFlags.None,  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 == 0)  continue// 如果是空字节则不做处理 
 
                                client.ReceivedByte.Add(b); 
                        } 
 
                         // 把当前接收到的数据转换为字符串。用于判断是否包含自定义的结束符 
                         string receivedString = UTF8Encoding.UTF8.GetString(client.Buffer, 0, 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, 0, client.Buffer.Length, 0,  new AsyncCallback(OnDataReceived), 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, 0, byteData.Length, SocketFlags.None,  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 
                        { 
                                 // 完成将信息发送到客户端的这个异步操作 
                                 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) 
                { 
                         // 在服务端记录异常信息,关闭导致异常的 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(0, 9999).ToString().PadLeft(4, '0'); 
                        txtInput.Text =  "hi"
 
                         // 实例化 Socket 
                        _socket =  new Socket(AddressFamily.InterNetwork, SocketType.Stream, 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, 0, 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(); 
                } 
        } 
}
 
 
 





     本文转自webabcd 51CTO博客,原文链接:http://blog.51cto.com/webabcd/343812 ,如需转载请自行联系原作者
相关文章
|
3月前
|
Python
python socket 简单通信
python socket 简单通信
43 1
|
3月前
|
网络协议 安全 网络安全
网络编程:基于socket的TCP/IP通信。
网络编程:基于socket的TCP/IP通信。
214 0
|
24天前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【10月更文挑战第10天】网络协议定义了机器间通信的标准格式,确保信息准确无损地传输。主要分为两种模型:OSI七层模型与TCP/IP模型。
|
2月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【9月更文挑战第14天】网络协议是机器间交流的约定格式,确保信息准确传达。主要模型有OSI七层与TCP/IP模型,通过分层简化复杂网络环境。IP地址全局定位设备,MAC地址则在本地网络中定位。网络分层后,数据包层层封装,经由不同层次协议处理,最终通过Socket系统调用在应用层解析和响应。
|
3月前
|
网络协议
socket编程(2) -- TCP通信
socket编程(2) -- TCP通信
39 0
|
5月前
|
Java API 开发者
Java网络编程基础与Socket通信实战
Java网络编程基础与Socket通信实战
|
4月前
|
Java API 开发者
Java网络编程基础与Socket通信实战
Java网络编程基础与Socket通信实战
|
4月前
|
Java 数据格式
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
82 0
|
4月前
|
网络协议 安全 Java
深入了解Java中的网络编程与Socket通信
深入了解Java中的网络编程与Socket通信
|
4月前
|
网络协议 安全 Java
深入了解Java中的网络编程与Socket通信
深入了解Java中的网络编程与Socket通信
下一篇
无影云桌面