/*===================================================================== 文件: Wintalk.cs
摘要: 演示如何使用 .NET创建聊天程序
=====================================================================*/
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 app 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); break; default: ShowUsage(); return false; } }catch{ ShowUsage(); return false; } return true; }
// Show sample usage private static void ShowUsage(){ MessageBox.Show("WinTalk [switch] [parameters...]/n/n"+ " /L [port]/t/t-- Listens on a port. Default: 5150/n"+ " /C [address] [port]/t-- Connects to an address and port./n/n"+ "Example Server - /n"+ "Wintalk /L/n/n"+ "Example Client - /n"+ "Wintalk /C ServerMachine 5150","WinTalk Usage"); }}
// UI class for the sampleclass 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 chattingclass 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 delegat 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.Resolve("127.0.0.1").AddressList[0], endPoint.Port); }else{ Notifications( Notification.Error, "Error Initializing Socket:/n"+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:/n"+e.Message); } }catch(Exception e){ Notifications(Notification.Error, "Socket Error:/n"+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; }
Microsoft.NET FrameworkSDK带这个例子.