您的位置:首页 > 其它

Socket + ThreadPool的例子

2007-02-12 18:13 281 查看
这是一个点对点的聊天工具,是学习Socket + ThreadPool的绝好例子。在实现里也可以学习到了方法委托,事件处理,窗口编程等等概念,是一个比较综合且易学的例子。代码如下:



using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Drawing;
using System.Windows.Forms;

class App
{
// Entry point
public static void Main(String[] args)
{
// If the args parse in known way then run the application
if (ParseArgs(args))
{
// Create a custom Talker object
Talker talker = new Talker(endPoint, client);
// Pass the object reference to a new form object
TalkForm form = new TalkForm(talker);
// Start the talker "talking"
talker.Start();

// Run the applications message pump
Application.Run(form);
}
}

// Parsed Argument Storage
private static IPEndPoint endPoint;
private static bool client;

// Parse command line arguments
private static bool ParseArgs(String[] args)
{
try
{
if (args.Length == 0)
{
client = false;
endPoint = new IPEndPoint(IPAddress.Any, 5150);
return true;
}

switch (Char.ToUpper(args[0][1]))
{
case 'L':
int port = 5150;
if (args.Length > 1)
{
port = Convert.ToInt32(args[1]);
}
endPoint = new IPEndPoint(IPAddress.Any, port);
client = false;
break;
case 'C':
port = 5150;
String address = "127.0.0.1";
client = true;
if (args.Length > 1)
{
address = args[1];
port = Convert.ToInt32(args[2]);
}
// endPoint = new IPEndPoint(Dns.Resolve(address).AddressList[0], port);
endPoint = new IPEndPoint(Dns.GetHostEntry(address).AddressList[0], port);
break;
default:
ShowUsage();
return false;
}
}
catch
{
ShowUsage();
return false;
}

return true;
}

// Show sample usage
private static void ShowUsage()
{
MessageBox.Show("WinTalk [switch] [parameters...] " + " /L [port] -- Listens on a port. Default: 5150 " +
" /C [address] [port] -- Connects to an address and port. " +
"Example Server - " +
"Wintalk /L " +
"Example Client - " +
"Wintalk /C ServerMachine 5150", "WinTalk Usage");
}
}

// UI class for the sample
class TalkForm : Form
{
public TalkForm(Talker talker)
{
// Associate for method with the talker object
this.talker = talker;
talker.Notifications += new
Talker.NotificationCallback(HandleTalkerNotifications);

// Create a UI elements
Splitter talkSplitter = new Splitter();
Panel talkPanel = new Panel();

receiveText = new TextBox();
sendText = new TextBox();

// we'll support up to 64k data in our text box controls
receiveText.MaxLength = sendText.MaxLength = 65536;
statusText = new Label();

// Initialize UI elements
receiveText.Dock = DockStyle.Top;
receiveText.Multiline = true;
receiveText.ScrollBars = ScrollBars.Both;
receiveText.Size = new Size(506, 192);
receiveText.TabIndex = 1;
receiveText.Text = "";
receiveText.WordWrap = false;
receiveText.ReadOnly = true;

talkPanel.Anchor = (AnchorStyles.Top | AnchorStyles.Bottom
| AnchorStyles.Left | AnchorStyles.Right);
talkPanel.Controls.AddRange(new Control[] {sendText,
talkSplitter,
receiveText});
talkPanel.Size = new Size(506, 371);
talkPanel.TabIndex = 0;

talkSplitter.Dock = DockStyle.Top;
talkSplitter.Location = new Point(0, 192);
talkSplitter.Size = new Size(506, 6);
talkSplitter.TabIndex = 2;
talkSplitter.TabStop = false;

statusText.Dock = DockStyle.Bottom;
statusText.Location = new Point(0, 377);
statusText.Size = new Size(507, 15);
statusText.TabIndex = 1;
statusText.Text = "Status:";

sendText.Dock = DockStyle.Fill;
sendText.Location = new Point(0, 198);
sendText.Multiline = true;
sendText.ScrollBars = ScrollBars.Both;
sendText.Size = new Size(506, 173);
sendText.TabIndex = 0;
sendText.Text = "";
sendText.WordWrap = false;
sendText.TextChanged += new EventHandler(HandleTextChange);
sendText.Enabled = false;

AutoScaleBaseSize = new Size(5, 13);
ClientSize = new Size(507, 392);
Controls.AddRange(new Control[] {statusText,
talkPanel});
Text = "WinTalk";

this.ActiveControl = sendText;
}

// When the app closes, dispose of the talker object
protected override void OnClosed(EventArgs e)
{
if (talker != null)
{
// remove our notification handler
talker.Notifications -= new
Talker.NotificationCallback(HandleTalkerNotifications);

talker.Dispose();
}
base.OnClosed(e);
}

// Handle notifications from the talker object
private void HandleTalkerNotifications(
Talker.Notification notify, Object data)
{
switch (notify)
{
case Talker.Notification.Initialized:
break;
// Respond to status changes
case Talker.Notification.StatusChange:
Talker.Status status = (Talker.Status)data;
statusText.Text = String.Format("Status: {0}", status);
if (status == Talker.Status.Connected)
{
sendText.Enabled = true;
}
break;
// Respond to received text
case Talker.Notification.Received:
receiveText.Text = data.ToString();
receiveText.SelectionStart = Int32.MaxValue;
receiveText.ScrollToCaret();
break;
// Respond to error notifications
case Talker.Notification.Error:
Close(data.ToString());
break;
// Respond to end
case Talker.Notification.End:
MessageBox.Show(data.ToString(), "Closing WinTalk");
Close();
break;
default:
Close();
break;
}
}

// Handle text change notifications and send talk
private void HandleTextChange(Object sender, EventArgs e)
{
if (talker != null)
{
talker.SendTalk((sender as TextBox).Text);
}
}

// Close with an explanation
private void Close(String message)
{
MessageBox.Show(message, "Error!");
Close();
}

// Private UI elements
private TextBox receiveText;
private TextBox sendText;
private Label statusText;
private Talker talker;
}

// An encapsulation of the Sockets class used for socket chatting
class Talker : IDisposable
{
// Construct a talker
public Talker(IPEndPoint endPoint, bool client)
{
this.endPoint = endPoint;
this.client = client;

socket = null;
reader = null;
writer = null;

statusText = prevSendText = prevReceiveText = String.Empty;
}

// Finalize a talker
~Talker()
{
Dispose();
}

// Dispose of resources and surpress finalization
public void Dispose()
{
GC.SuppressFinalize(this);
if (reader != null)
{
reader.Close();
reader = null;
}
if (writer != null)
{
writer.Close();
writer = null;
}
if (socket != null)
{

socket.Close();
socket = null;
}
}

// Nested delegate class and matchine event
public delegate
void NotificationCallback(Notification notify, Object data);
public event NotificationCallback Notifications;

// Nested enum for notifications
public enum Notification
{
Initialized = 1,
StatusChange,
Received,
End,
Error
}

// Nested enum for supported states
public enum Status
{
Listening,
Connected
}

// Start up the talker's functionality
public void Start()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(EstablishSocket));
}

// Send text to remote connection
public void SendTalk(String newText)
{
String send;
// Is this an append
if ((prevSendText.Length <= newText.Length) && String.CompareOrdinal(
newText, 0, prevSendText, 0, prevSendText.Length) == 0)
{
String append = newText.Substring(prevSendText.Length);
send = String.Format("A{0}:{1}", append.Length, append);
// or a complete replacement
}
else
{
send = String.Format("R{0}:{1}", newText.Length, newText);
}
// Send the data and flush it out
writer.Write(send);
writer.Flush();
// Save the text for future comparison
prevSendText = newText;
}

// Send a status notification
private void SetStatus(Status status)
{
this.status = status;
Notifications(Notification.StatusChange, status);
}

// Establish a socket connection and start receiving
private void EstablishSocket(Object state)
{
try
{
// If not client, setup listner
if (!client)
{
Socket listener;

try
{
listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
listener.Blocking = true;
listener.Bind(endPoint);
SetStatus(Status.Listening);
listener.Listen(0);
socket = listener.Accept();
listener.Close();
}
catch (SocketException e)
{
// If there is already a listener on this port try client
if (e.ErrorCode == 10048)
{
client = true;
endPoint = new IPEndPoint(
Dns.GetHostEntry("127.0.0.1").AddressList[0], endPoint.Port);
}
else
{
Notifications(
Notification.Error,
"Error Initializing Socket: " + e.ToString());
}
}
}

// Try a client connection
if (client)
{
Socket temp = new
Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
temp.Blocking = true;
temp.Connect(endPoint);
socket = temp;
}

// If it all worked out, create stream objects
if (socket != null)
{
SetStatus(Status.Connected);
NetworkStream stream = new NetworkStream(socket);
reader = new StreamReader(stream);
writer = new StreamWriter(stream);
Notifications(Notification.Initialized, this);
}
else
{
Notifications(Notification.Error,
"Failed to Establish Socket");
}

// Start receiving talk
// Note: on w2k and later platforms, the NetworkStream.Read()
// method called in ReceiveTalk will generate an exception when
// the remote connection closes. We handle this case in our
// catch block below.
ReceiveTalk();

// On Win9x platforms, NetworkStream.Read() returns 0 when
// the remote connection closes, prompting a graceful return
// from ReceiveTalk() above. We will generate a Notification.End
// message here to handle the case and shut down the remaining
// WinTalk instance.
Notifications(Notification.End, "Remote connection has closed.");

}
catch (IOException e)
{
SocketException sockExcept = e.InnerException as SocketException;
if (sockExcept != null && 10054 == sockExcept.ErrorCode)
{
Notifications(Notification.End, "Remote connection has closed.");
}
else
{
if (Notifications != null)
Notifications(Notification.Error, "Socket Error: " + e.Message);

}
}
catch (Exception e)
{
Notifications(Notification.Error, "Socket Error: " + e.Message);
}
}

// Receive chat from remote client
private void ReceiveTalk()
{
char[] commandBuffer = new char[20];
char[] oneBuffer = new char[1];
int readMode = 1;
int counter = 0;
StringBuilder text = new StringBuilder();

while (readMode != 0)
{
if (reader.Read(oneBuffer, 0, 1) == 0)
{
readMode = 0;
continue;
}

switch (readMode)
{
case 1:
if (counter == commandBuffer.Length)
{
readMode = 0;
continue;
}
if (oneBuffer[0] != ':')
{
commandBuffer[counter++] = oneBuffer[0];
}
else
{
counter = Convert.ToInt32(
new String(commandBuffer, 1, counter - 1));
if (counter > 0)
{
readMode = 2;
text.Length = 0;
}
else if (commandBuffer[0] == 'R')
{
counter = 0;
prevReceiveText = String.Empty;
Notifications(Notification.Received, prevReceiveText);
}
}
break;
case 2:
text.Append(oneBuffer[0]);
if (--counter == 0)
{
switch (commandBuffer[0])
{
case 'R':
prevReceiveText = text.ToString();
break;
default:
prevReceiveText += text.ToString();
break;
}
readMode = 1;

Notifications(Notification.Received, prevReceiveText);
}
break;
default:
readMode = 0;
continue;
}
}
}

private Socket socket;

private TextReader reader;
private TextWriter writer;

bool client;
IPEndPoint endPoint;

private String prevSendText;
private String prevReceiveText;
private String statusText;

private Status status;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: