1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 Change History (most recent first):
19 $Log: SimpleChat.cs,v $
20 Revision 1.6 2006/08/14 23:24:21 cheshire
21 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
23 Revision 1.5 2004/09/13 19:37:42 shersche
24 Change code to reflect namespace and type changes to dnssd.NET library
26 Revision 1.4 2004/09/11 05:42:56 shersche
27 don't reset SelectedIndex in OnRemove
29 Revision 1.3 2004/09/11 00:38:58 shersche
30 DNSService APIs now expect port in host format
32 Revision 1.2 2004/07/19 22:08:53 shersche
33 Fixed rdata->int conversion problem in QueryRecordReply
35 Revision 1.1 2004/07/19 07:57:08 shersche
44 using System.Collections;
45 using System.ComponentModel;
46 using System.Windows.Forms;
48 using System.Net.Sockets;
53 namespace SimpleChat.NET
56 /// Summary description for Form1.
63 // Holds onto the information associated with a peer on the network
67 public int InterfaceIndex;
71 public IPAddress Address;
74 public override String
87 if ((object) this == other)
91 else if (other is PeerData)
93 PeerData otherPeerData = (PeerData) other;
95 result = (this.Name == otherPeerData.Name);
105 return Name.GetHashCode();
112 // Holds onto the information associated with the resolution
115 public class ResolveData
117 public int InterfaceIndex;
118 public String FullName;
119 public String HostName;
121 public Byte[] TxtRecord;
123 public override String
134 // Holds onto the data associated with an asynchronous
137 class SocketStateObject
139 public const int BUFFER_SIZE = 1024;
140 private Socket m_socket;
141 public byte[] m_buffer;
142 public bool m_complete;
143 public StringBuilder m_sb = new StringBuilder();
145 public SocketStateObject(Socket socket)
147 m_buffer = new byte[BUFFER_SIZE];
161 public class Form1 : System.Windows.Forms.Form
163 private System.Windows.Forms.ComboBox comboBox1;
164 private System.Windows.Forms.TextBox textBox2;
165 private System.Windows.Forms.Button button1;
166 private System.Windows.Forms.Label label1;
167 private ServiceRef registrar = null;
168 private ServiceRef browser = null;
169 private ServiceRef resolver = null;
170 private String myName;
172 /// Required designer variable.
174 private System.ComponentModel.Container components = null;
177 // These all of our callbacks. These are invoked in the context
178 // of the main (GUI) thread. The DNSService callbacks Invoke()
180 delegate void RegisterServiceCallback(String name);
181 delegate void AddPeerCallback(PeerData data);
182 delegate void RemovePeerCallback(PeerData data);
183 delegate void ResolveServiceCallback(ResolveData data);
184 delegate void ResolveAddressCallback(System.Net.IPAddress address);
185 delegate void ReadMessageCallback(String data);
187 RegisterServiceCallback registerServiceCallback;
188 AddPeerCallback addPeerCallback;
189 RemovePeerCallback removePeerCallback;
190 ResolveServiceCallback resolveServiceCallback;
191 ResolveAddressCallback resolveAddressCallback;
192 ReadMessageCallback readMessageCallback;
193 private System.Windows.Forms.RichTextBox richTextBox1;
196 // The socket that we will be reading data from
198 Socket socket = null;
203 // The name that we are passed might be different than the
204 // name we called Register with. So we hold onto this name
205 // rather than the name we Register with.
207 // This is called (indirectly) from OnRegisterReply().
221 // Called when DNSServices detects a new P2P Chat peer has
224 // This is called (indirectly) from OnBrowseReply()
232 comboBox1.Items.Add(peer);
234 if (comboBox1.Items.Count == 1)
236 comboBox1.SelectedIndex = 0;
243 // Called when DNSServices detects a P2P peer has left
246 // This is called (indirectly) from OnBrowseReply()
254 comboBox1.Items.Remove(peer);
260 // Called when DNSServices has resolved a service.
262 // This is called (indirectly) from OnResolveService()
272 PeerData peer = (PeerData) comboBox1.SelectedItem;
274 peer.Port = data.Port;
278 resolver = DNSService.QueryRecord(0, 0, data.HostName, /* ns_t_a */ 1, /* ns_t_c */ 1, new DNSService.QueryRecordReply(OnQueryRecordReply));
282 MessageBox.Show("QueryRecord Failed", "Error");
290 // Called when DNSServices has finished a query operation
292 // This is called (indirectly) from OnQueryRecordReply()
297 System.Net.IPAddress address
302 PeerData peer = (PeerData) comboBox1.SelectedItem;
304 peer.Address = address;
310 // Called when there is data to be read on a socket
312 // This is called (indirectly) from OnReadSocket()
322 for (int i = 0; i < msg.Length && msg[i] != ':'; i++)
324 rgb = rgb ^ ((int) msg[i] << (i % 3 + 2) * 8);
327 Color color = Color.FromArgb(rgb & 0x007F7FFF);
329 richTextBox1.SelectionColor = color;
331 richTextBox1.AppendText(msg + "\n");
337 // Called by DNSServices core as a result of DNSService.Register()
340 // This is called from a worker thread by DNSService core.
352 if (errorCode == ErrorCode.NoError)
354 Invoke(registerServiceCallback, new Object[]{name});
358 MessageBox.Show("OnRegisterReply returned an error code " + errorCode, "Error");
366 // Called by DNSServices core as a result of DNSService.Browse()
369 // This is called from a worker thread by DNSService core.
382 if (errorCode == ErrorCode.NoError)
384 PeerData peer = new PeerData();
386 peer.InterfaceIndex = interfaceIndex;
389 peer.Domain = domain;
392 if ((flags & ServiceFlags.Add) != 0)
394 Invoke(addPeerCallback, new Object[]{peer});
396 else if ((flags == 0) || ((flags & ServiceFlags.MoreComing) != 0))
398 Invoke(removePeerCallback, new Object[]{peer});
403 MessageBox.Show("OnBrowseReply returned an error code " + errorCode, "Error");
410 // Called by DNSServices core as a result of DNSService.Resolve()
413 // This is called from a worker thread by DNSService core.
428 if (errorCode == ErrorCode.NoError)
430 ResolveData data = new ResolveData();
432 data.InterfaceIndex = interfaceIndex;
433 data.FullName = fullName;
434 data.HostName = hostName;
436 data.TxtRecord = txtRecord;
438 Invoke(resolveServiceCallback, new Object[]{data});
442 MessageBox.Show("OnResolveReply returned an error code: " + errorCode, "Error");
447 // OnQueryRecordReply
449 // Called by DNSServices core as a result of DNSService.QueryRecord()
452 // This is called from a worker thread by DNSService core.
468 if (errorCode == ErrorCode.NoError)
470 uint bits = BitConverter.ToUInt32(rdata, 0);
471 System.Net.IPAddress data = new System.Net.IPAddress(bits);
473 Invoke(resolveAddressCallback, new Object[]{data});
477 MessageBox.Show("OnQueryRecordReply returned an error code: " + errorCode, "Error");
484 // Called by the .NET core when there is data to be read on a socket
486 // This is called from a worker thread by the .NET core
494 SocketStateObject so = (SocketStateObject) ar.AsyncState;
495 Socket s = so.WorkSocket;
504 int read = s.EndReceive(ar);
508 String msg = Encoding.UTF8.GetString(so.m_buffer, 0, read);
510 Invoke(readMessageCallback, new Object[]{msg});
513 s.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(OnReadSocket), so);
524 // Required for Windows Form Designer support
526 InitializeComponent();
528 registerServiceCallback = new RegisterServiceCallback(OnRegisterService);
529 addPeerCallback = new AddPeerCallback(OnAddPeer);
530 removePeerCallback = new RemovePeerCallback(OnRemovePeer);
531 resolveServiceCallback = new ResolveServiceCallback(OnResolveService);
532 resolveAddressCallback = new ResolveAddressCallback(OnResolveAddress);
533 readMessageCallback = new ReadMessageCallback(OnReadMessage);
535 this.Load += new System.EventHandler(this.Form1_Load);
537 this.AcceptButton = button1;
541 /// Clean up any resources being used.
543 protected override void
544 Dispose( bool disposing )
548 if (components != null)
550 components.Dispose();
553 if (registrar != null)
563 base.Dispose( disposing );
566 #region Windows Form Designer generated code
568 /// Required method for Designer support - do not modify
569 /// the contents of this method with the code editor.
571 private void InitializeComponent()
573 this.comboBox1 = new System.Windows.Forms.ComboBox();
574 this.textBox2 = new System.Windows.Forms.TextBox();
575 this.button1 = new System.Windows.Forms.Button();
576 this.label1 = new System.Windows.Forms.Label();
577 this.richTextBox1 = new System.Windows.Forms.RichTextBox();
578 this.SuspendLayout();
582 this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
583 | System.Windows.Forms.AnchorStyles.Right);
584 this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
585 this.comboBox1.Location = new System.Drawing.Point(59, 208);
586 this.comboBox1.Name = "comboBox1";
587 this.comboBox1.Size = new System.Drawing.Size(224, 21);
588 this.comboBox1.Sorted = true;
589 this.comboBox1.TabIndex = 5;
590 this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
594 this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
595 | System.Windows.Forms.AnchorStyles.Right);
596 this.textBox2.Location = new System.Drawing.Point(8, 248);
597 this.textBox2.Name = "textBox2";
598 this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Horizontal;
599 this.textBox2.Size = new System.Drawing.Size(192, 20);
600 this.textBox2.TabIndex = 2;
601 this.textBox2.Text = "";
602 this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged);
606 this.button1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right);
607 this.button1.Enabled = false;
608 this.button1.Location = new System.Drawing.Point(208, 248);
609 this.button1.Name = "button1";
610 this.button1.TabIndex = 3;
611 this.button1.Text = "Send";
612 this.button1.Click += new System.EventHandler(this.button1_Click);
616 this.label1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left);
617 this.label1.Location = new System.Drawing.Point(8, 210);
618 this.label1.Name = "label1";
619 this.label1.Size = new System.Drawing.Size(48, 16);
620 this.label1.TabIndex = 4;
621 this.label1.Text = "Talk To:";
625 this.richTextBox1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
626 | System.Windows.Forms.AnchorStyles.Left)
627 | System.Windows.Forms.AnchorStyles.Right);
628 this.richTextBox1.Location = new System.Drawing.Point(8, 8);
629 this.richTextBox1.Name = "richTextBox1";
630 this.richTextBox1.ReadOnly = true;
631 this.richTextBox1.Size = new System.Drawing.Size(272, 184);
632 this.richTextBox1.TabIndex = 1;
633 this.richTextBox1.Text = "";
637 this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
638 this.ClientSize = new System.Drawing.Size(292, 273);
639 this.Controls.AddRange(new System.Windows.Forms.Control[] {
646 this.Text = "SimpleChat.NET";
647 this.ResumeLayout(false);
652 private void Form1_Load(object sender, EventArgs e)
654 IPEndPoint localEP = new IPEndPoint(System.Net.IPAddress.Any, 0);
657 // create the socket and bind to INADDR_ANY
659 socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
660 socket.Bind(localEP);
661 localEP = (IPEndPoint) socket.LocalEndPoint;
664 // start asynchronous read
666 SocketStateObject so = new SocketStateObject(socket);
667 socket.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(this.OnReadSocket), so);
672 // start the register and browse operations
674 registrar = DNSService.Register(0, 0, System.Environment.UserName, "_p2pchat._udp", null, null, localEP.Port, null, new DNSService.RegisterReply(OnRegisterReply));
675 browser = DNSService.Browse(0, 0, "_p2pchat._udp", null, new DNSService.BrowseReply(OnBrowseReply));
679 MessageBox.Show("DNSServices Not Available", "Error");
685 /// The main entry point for the application.
690 Application.Run(new Form1());
694 // send the message to a peer
696 private void button1_Click(object sender, System.EventArgs e)
698 PeerData peer = (PeerData) comboBox1.SelectedItem;
700 String message = myName + ": " + textBox2.Text;
702 Byte[] bytes = Encoding.UTF8.GetBytes(message);
704 UdpClient udpSocket = new UdpClient(peer.Address.ToString(), peer.Port);
706 udpSocket.Send(bytes, bytes.Length);
708 richTextBox1.SelectionColor = Color.Black;
710 richTextBox1.AppendText(textBox2.Text + "\n");
716 // called when typing in message box
718 private void textBox2_TextChanged(object sender, System.EventArgs e)
720 PeerData peer = (PeerData) comboBox1.SelectedItem;
722 if ((peer.Address != null) && (textBox2.Text.Length > 0))
724 button1.Enabled = true;
728 button1.Enabled = false;
733 // called when peer target changes
737 /// <param name="sender"></param>
738 /// <param name="e"></param>
739 private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
741 PeerData peer = (PeerData) comboBox1.SelectedItem;
745 resolver = DNSService.Resolve(0, 0, peer.Name, peer.Type, peer.Domain, new DNSService.ResolveReply(OnResolveReply));
749 MessageBox.Show("Unable to Resolve service", "Error");