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;
// 完成接收資料的這個異步操作,并傳回接收的位元組數
if (client.Socket.Connected)
count = client.Socket.EndReceive(async);
// 把接收到的資料添加進收到的位元組集合内
// 本例采用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);
// 繼續開始接收用戶端傳入的資料
client.Socket.BeginReceive(client.Buffer, 0, client.Buffer.Length, 0, new AsyncCallback(OnDataReceived), client);
/// <summary>
/// 發送資料到所有連入的用戶端
/// </summary>
/// <param name="data">需要發送的資料</param>
private void SendData(string data)
byte[] byteData = UTF8Encoding.UTF8.GetBytes(data);
foreach (ClientSocketPacket client in _clientList)
{
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)
// 完成将資訊發送到用戶端的這個異步操作
client.Socket.EndSend(async);
/// 處理 SocketException 異常
/// <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.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;
namespace Silverlight20.Communication
public partial class SocketClient : UserControl
// 資訊結束符,用于判斷是否完整地讀取了使用者發送的資訊(要與服務端的資訊結束符相對應)
// 用戶端 Socket
private Socket _socket;
// Socket 異步操作對象
private SocketAsyncEventArgs _sendEventArgs;
public SocketClient()
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)
// 将接收到的資料轉換為字元串
string data = UTF8Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
WriteText(data);
catch (Exception ex)
WriteText(ex.ToString());
// 繼續異步地從服務端 Socket 接收資料
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);
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)
// 按了Enter鍵就向服務端發送資料
if (e.Key == Key.Enter)
SendData();
OK
<a href="http://down.51cto.com/data/100302" target="_blank">[源碼下載下傳]</a>
本文轉自webabcd 51CTO部落格,原文連結:http://blog.51cto.com/webabcd/343812,如需轉載請自行聯系原作者