/*=====================================================================
文件: 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带这个例子.
新闻热点
疑难解答