/*=====================================================================
文件: 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 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 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帶這個例子.