2 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: SimpleChat.cs,v $
26 Revision 1.5 2004/09/13 19:37:42 shersche
27 Change code to reflect namespace and type changes to dnssd.NET library
29 Revision 1.4 2004/09/11 05:42:56 shersche
30 don't reset SelectedIndex in OnRemove
32 Revision 1.3 2004/09/11 00:38:58 shersche
33 DNSService APIs now expect port in host format
35 Revision 1.2 2004/07/19 22:08:53 shersche
36 Fixed rdata->int conversion problem in QueryRecordReply
38 Revision 1.1 2004/07/19 07:57:08 shersche
47 using System.Collections;
48 using System.ComponentModel;
49 using System.Windows.Forms;
51 using System.Net.Sockets;
56 namespace SimpleChat.NET
59 /// Summary description for Form1.
66 // Holds onto the information associated with a peer on the network
70 public int InterfaceIndex;
74 public IPAddress Address;
77 public override String
90 if ((object) this == other)
94 else if (other is PeerData)
96 PeerData otherPeerData = (PeerData) other;
98 result = (this.Name == otherPeerData.Name);
108 return Name.GetHashCode();
115 // Holds onto the information associated with the resolution
118 public class ResolveData
120 public int InterfaceIndex;
121 public String FullName;
122 public String HostName;
124 public Byte[] TxtRecord;
126 public override String
137 // Holds onto the data associated with an asynchronous
140 class SocketStateObject
142 public const int BUFFER_SIZE = 1024;
143 private Socket m_socket;
144 public byte[] m_buffer;
145 public bool m_complete;
146 public StringBuilder m_sb = new StringBuilder();
148 public SocketStateObject(Socket socket)
150 m_buffer = new byte[BUFFER_SIZE];
164 public class Form1 : System.Windows.Forms.Form
166 private System.Windows.Forms.ComboBox comboBox1;
167 private System.Windows.Forms.TextBox textBox2;
168 private System.Windows.Forms.Button button1;
169 private System.Windows.Forms.Label label1;
170 private ServiceRef registrar = null;
171 private ServiceRef browser = null;
172 private ServiceRef resolver = null;
173 private String myName;
175 /// Required designer variable.
177 private System.ComponentModel.Container components = null;
180 // These all of our callbacks. These are invoked in the context
181 // of the main (GUI) thread. The DNSService callbacks Invoke()
183 delegate void RegisterServiceCallback(String name);
184 delegate void AddPeerCallback(PeerData data);
185 delegate void RemovePeerCallback(PeerData data);
186 delegate void ResolveServiceCallback(ResolveData data);
187 delegate void ResolveAddressCallback(System.Net.IPAddress address);
188 delegate void ReadMessageCallback(String data);
190 RegisterServiceCallback registerServiceCallback;
191 AddPeerCallback addPeerCallback;
192 RemovePeerCallback removePeerCallback;
193 ResolveServiceCallback resolveServiceCallback;
194 ResolveAddressCallback resolveAddressCallback;
195 ReadMessageCallback readMessageCallback;
196 private System.Windows.Forms.RichTextBox richTextBox1;
199 // The socket that we will be reading data from
201 Socket socket = null;
206 // The name that we are passed might be different than the
207 // name we called Register with. So we hold onto this name
208 // rather than the name we Register with.
210 // This is called (indirectly) from OnRegisterReply().
224 // Called when DNSServices detects a new P2P Chat peer has
227 // This is called (indirectly) from OnBrowseReply()
235 comboBox1.Items.Add(peer);
237 if (comboBox1.Items.Count == 1)
239 comboBox1.SelectedIndex = 0;
246 // Called when DNSServices detects a P2P peer has left
249 // This is called (indirectly) from OnBrowseReply()
257 comboBox1.Items.Remove(peer);
263 // Called when DNSServices has resolved a service.
265 // This is called (indirectly) from OnResolveService()
275 PeerData peer = (PeerData) comboBox1.SelectedItem;
277 peer.Port = data.Port;
281 resolver = DNSService.QueryRecord(0, 0, data.HostName, /* ns_t_a */ 1, /* ns_t_c */ 1, new DNSService.QueryRecordReply(OnQueryRecordReply));
285 MessageBox.Show("QueryRecord Failed", "Error");
293 // Called when DNSServices has finished a query operation
295 // This is called (indirectly) from OnQueryRecordReply()
300 System.Net.IPAddress address
305 PeerData peer = (PeerData) comboBox1.SelectedItem;
307 peer.Address = address;
313 // Called when there is data to be read on a socket
315 // This is called (indirectly) from OnReadSocket()
325 for (int i = 0; i < msg.Length && msg[i] != ':'; i++)
327 rgb = rgb ^ ((int) msg[i] << (i % 3 + 2) * 8);
330 Color color = Color.FromArgb(rgb & 0x007F7FFF);
332 richTextBox1.SelectionColor = color;
334 richTextBox1.AppendText(msg + "\n");
340 // Called by DNSServices core as a result of DNSService.Register()
343 // This is called from a worker thread by DNSService core.
355 if (errorCode == ErrorCode.NoError)
357 Invoke(registerServiceCallback, new Object[]{name});
361 MessageBox.Show("OnRegisterReply returned an error code " + errorCode, "Error");
369 // Called by DNSServices core as a result of DNSService.Browse()
372 // This is called from a worker thread by DNSService core.
385 if (errorCode == ErrorCode.NoError)
387 PeerData peer = new PeerData();
389 peer.InterfaceIndex = interfaceIndex;
392 peer.Domain = domain;
395 if ((flags & ServiceFlags.Add) != 0)
397 Invoke(addPeerCallback, new Object[]{peer});
399 else if ((flags == 0) || ((flags & ServiceFlags.MoreComing) != 0))
401 Invoke(removePeerCallback, new Object[]{peer});
406 MessageBox.Show("OnBrowseReply returned an error code " + errorCode, "Error");
413 // Called by DNSServices core as a result of DNSService.Resolve()
416 // This is called from a worker thread by DNSService core.
431 if (errorCode == ErrorCode.NoError)
433 ResolveData data = new ResolveData();
435 data.InterfaceIndex = interfaceIndex;
436 data.FullName = fullName;
437 data.HostName = hostName;
439 data.TxtRecord = txtRecord;
441 Invoke(resolveServiceCallback, new Object[]{data});
445 MessageBox.Show("OnResolveReply returned an error code: " + errorCode, "Error");
450 // OnQueryRecordReply
452 // Called by DNSServices core as a result of DNSService.QueryRecord()
455 // This is called from a worker thread by DNSService core.
471 if (errorCode == ErrorCode.NoError)
473 uint bits = BitConverter.ToUInt32(rdata, 0);
474 System.Net.IPAddress data = new System.Net.IPAddress(bits);
476 Invoke(resolveAddressCallback, new Object[]{data});
480 MessageBox.Show("OnQueryRecordReply returned an error code: " + errorCode, "Error");
487 // Called by the .NET core when there is data to be read on a socket
489 // This is called from a worker thread by the .NET core
497 SocketStateObject so = (SocketStateObject) ar.AsyncState;
498 Socket s = so.WorkSocket;
507 int read = s.EndReceive(ar);
511 String msg = Encoding.UTF8.GetString(so.m_buffer, 0, read);
513 Invoke(readMessageCallback, new Object[]{msg});
516 s.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(OnReadSocket), so);
527 // Required for Windows Form Designer support
529 InitializeComponent();
531 registerServiceCallback = new RegisterServiceCallback(OnRegisterService);
532 addPeerCallback = new AddPeerCallback(OnAddPeer);
533 removePeerCallback = new RemovePeerCallback(OnRemovePeer);
534 resolveServiceCallback = new ResolveServiceCallback(OnResolveService);
535 resolveAddressCallback = new ResolveAddressCallback(OnResolveAddress);
536 readMessageCallback = new ReadMessageCallback(OnReadMessage);
538 this.Load += new System.EventHandler(this.Form1_Load);
540 this.AcceptButton = button1;
544 /// Clean up any resources being used.
546 protected override void
547 Dispose( bool disposing )
551 if (components != null)
553 components.Dispose();
556 if (registrar != null)
566 base.Dispose( disposing );
569 #region Windows Form Designer generated code
571 /// Required method for Designer support - do not modify
572 /// the contents of this method with the code editor.
574 private void InitializeComponent()
576 this.comboBox1 = new System.Windows.Forms.ComboBox();
577 this.textBox2 = new System.Windows.Forms.TextBox();
578 this.button1 = new System.Windows.Forms.Button();
579 this.label1 = new System.Windows.Forms.Label();
580 this.richTextBox1 = new System.Windows.Forms.RichTextBox();
581 this.SuspendLayout();
585 this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
586 | System.Windows.Forms.AnchorStyles.Right);
587 this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
588 this.comboBox1.Location = new System.Drawing.Point(59, 208);
589 this.comboBox1.Name = "comboBox1";
590 this.comboBox1.Size = new System.Drawing.Size(224, 21);
591 this.comboBox1.Sorted = true;
592 this.comboBox1.TabIndex = 5;
593 this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
597 this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
598 | System.Windows.Forms.AnchorStyles.Right);
599 this.textBox2.Location = new System.Drawing.Point(8, 248);
600 this.textBox2.Name = "textBox2";
601 this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Horizontal;
602 this.textBox2.Size = new System.Drawing.Size(192, 20);
603 this.textBox2.TabIndex = 2;
604 this.textBox2.Text = "";
605 this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged);
609 this.button1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right);
610 this.button1.Enabled = false;
611 this.button1.Location = new System.Drawing.Point(208, 248);
612 this.button1.Name = "button1";
613 this.button1.TabIndex = 3;
614 this.button1.Text = "Send";
615 this.button1.Click += new System.EventHandler(this.button1_Click);
619 this.label1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left);
620 this.label1.Location = new System.Drawing.Point(8, 210);
621 this.label1.Name = "label1";
622 this.label1.Size = new System.Drawing.Size(48, 16);
623 this.label1.TabIndex = 4;
624 this.label1.Text = "Talk To:";
628 this.richTextBox1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
629 | System.Windows.Forms.AnchorStyles.Left)
630 | System.Windows.Forms.AnchorStyles.Right);
631 this.richTextBox1.Location = new System.Drawing.Point(8, 8);
632 this.richTextBox1.Name = "richTextBox1";
633 this.richTextBox1.ReadOnly = true;
634 this.richTextBox1.Size = new System.Drawing.Size(272, 184);
635 this.richTextBox1.TabIndex = 1;
636 this.richTextBox1.Text = "";
640 this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
641 this.ClientSize = new System.Drawing.Size(292, 273);
642 this.Controls.AddRange(new System.Windows.Forms.Control[] {
649 this.Text = "SimpleChat.NET";
650 this.ResumeLayout(false);
655 private void Form1_Load(object sender, EventArgs e)
657 IPEndPoint localEP = new IPEndPoint(System.Net.IPAddress.Any, 0);
660 // create the socket and bind to INADDR_ANY
662 socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
663 socket.Bind(localEP);
664 localEP = (IPEndPoint) socket.LocalEndPoint;
667 // start asynchronous read
669 SocketStateObject so = new SocketStateObject(socket);
670 socket.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(this.OnReadSocket), so);
675 // start the register and browse operations
677 registrar = DNSService.Register(0, 0, System.Environment.UserName, "_p2pchat._udp", null, null, localEP.Port, null, new DNSService.RegisterReply(OnRegisterReply));
678 browser = DNSService.Browse(0, 0, "_p2pchat._udp", null, new DNSService.BrowseReply(OnBrowseReply));
682 MessageBox.Show("DNSServices Not Available", "Error");
688 /// The main entry point for the application.
693 Application.Run(new Form1());
697 // send the message to a peer
699 private void button1_Click(object sender, System.EventArgs e)
701 PeerData peer = (PeerData) comboBox1.SelectedItem;
703 String message = myName + ": " + textBox2.Text;
705 Byte[] bytes = Encoding.UTF8.GetBytes(message);
707 UdpClient udpSocket = new UdpClient(peer.Address.ToString(), peer.Port);
709 udpSocket.Send(bytes, bytes.Length);
711 richTextBox1.SelectionColor = Color.Black;
713 richTextBox1.AppendText(textBox2.Text + "\n");
719 // called when typing in message box
721 private void textBox2_TextChanged(object sender, System.EventArgs e)
723 PeerData peer = (PeerData) comboBox1.SelectedItem;
725 if ((peer.Address != null) && (textBox2.Text.Length > 0))
727 button1.Enabled = true;
731 button1.Enabled = false;
736 // called when peer target changes
740 /// <param name="sender"></param>
741 /// <param name="e"></param>
742 private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
744 PeerData peer = (PeerData) comboBox1.SelectedItem;
748 resolver = DNSService.Resolve(0, 0, peer.Name, peer.Type, peer.Domain, new DNSService.ResolveReply(OnResolveReply));
752 MessageBox.Show("Unable to Resolve service", "Error");