/* * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ Change History (most recent first): $Log: SimpleChat.cs,v $ Revision 1.5 2004/09/13 19:37:42 shersche Change code to reflect namespace and type changes to dnssd.NET library Revision 1.4 2004/09/11 05:42:56 shersche don't reset SelectedIndex in OnRemove Revision 1.3 2004/09/11 00:38:58 shersche DNSService APIs now expect port in host format Revision 1.2 2004/07/19 22:08:53 shersche Fixed rdata->int conversion problem in QueryRecordReply Revision 1.1 2004/07/19 07:57:08 shersche Initial revision */ using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Net; using System.Net.Sockets; using System.Data; using System.Text; using Apple.DNSSD; namespace SimpleChat.NET { /// /// Summary description for Form1. /// /// // // PeerData // // Holds onto the information associated with a peer on the network // public class PeerData { public int InterfaceIndex; public String Name; public String Type; public String Domain; public IPAddress Address; public int Port; public override String ToString() { return Name; } public override bool Equals(object other) { bool result = false; if (other != null) { if ((object) this == other) { result = true; } else if (other is PeerData) { PeerData otherPeerData = (PeerData) other; result = (this.Name == otherPeerData.Name); } } return result; } public override int GetHashCode() { return Name.GetHashCode(); } }; // // ResolveData // // Holds onto the information associated with the resolution // of a DNSService // public class ResolveData { public int InterfaceIndex; public String FullName; public String HostName; public int Port; public Byte[] TxtRecord; public override String ToString() { return FullName; } }; // // SocketStateObject // // Holds onto the data associated with an asynchronous // socket operation // class SocketStateObject { public const int BUFFER_SIZE = 1024; private Socket m_socket; public byte[] m_buffer; public bool m_complete; public StringBuilder m_sb = new StringBuilder(); public SocketStateObject(Socket socket) { m_buffer = new byte[BUFFER_SIZE]; m_complete = false; m_socket = socket; } public Socket WorkSocket { get { return m_socket; } } } public class Form1 : System.Windows.Forms.Form { private System.Windows.Forms.ComboBox comboBox1; private System.Windows.Forms.TextBox textBox2; private System.Windows.Forms.Button button1; private System.Windows.Forms.Label label1; private ServiceRef registrar = null; private ServiceRef browser = null; private ServiceRef resolver = null; private String myName; /// /// Required designer variable. /// private System.ComponentModel.Container components = null; // // These all of our callbacks. These are invoked in the context // of the main (GUI) thread. The DNSService callbacks Invoke() // them delegate void RegisterServiceCallback(String name); delegate void AddPeerCallback(PeerData data); delegate void RemovePeerCallback(PeerData data); delegate void ResolveServiceCallback(ResolveData data); delegate void ResolveAddressCallback(System.Net.IPAddress address); delegate void ReadMessageCallback(String data); RegisterServiceCallback registerServiceCallback; AddPeerCallback addPeerCallback; RemovePeerCallback removePeerCallback; ResolveServiceCallback resolveServiceCallback; ResolveAddressCallback resolveAddressCallback; ReadMessageCallback readMessageCallback; private System.Windows.Forms.RichTextBox richTextBox1; // // The socket that we will be reading data from // Socket socket = null; // // OnRegisterService // // The name that we are passed might be different than the // name we called Register with. So we hold onto this name // rather than the name we Register with. // // This is called (indirectly) from OnRegisterReply(). // private void OnRegisterService ( String name ) { myName = name; } // // OnAddPeer // // Called when DNSServices detects a new P2P Chat peer has // joined. // // This is called (indirectly) from OnBrowseReply() // private void OnAddPeer ( PeerData peer ) { comboBox1.Items.Add(peer); if (comboBox1.Items.Count == 1) { comboBox1.SelectedIndex = 0; } } // // OnRemovePeer // // Called when DNSServices detects a P2P peer has left // the network // // This is called (indirectly) from OnBrowseReply() // private void OnRemovePeer ( PeerData peer ) { comboBox1.Items.Remove(peer); } // // OnResolveService // // Called when DNSServices has resolved a service. // // This is called (indirectly) from OnResolveService() // private void OnResolveService ( ResolveData data ) { resolver.Dispose(); PeerData peer = (PeerData) comboBox1.SelectedItem; peer.Port = data.Port; try { resolver = DNSService.QueryRecord(0, 0, data.HostName, /* ns_t_a */ 1, /* ns_t_c */ 1, new DNSService.QueryRecordReply(OnQueryRecordReply)); } catch { MessageBox.Show("QueryRecord Failed", "Error"); Application.Exit(); } } // // OnResolveAddress // // Called when DNSServices has finished a query operation // // This is called (indirectly) from OnQueryRecordReply() // private void OnResolveAddress ( System.Net.IPAddress address ) { resolver.Dispose(); PeerData peer = (PeerData) comboBox1.SelectedItem; peer.Address = address; } // // OnReadMessage // // Called when there is data to be read on a socket // // This is called (indirectly) from OnReadSocket() // private void OnReadMessage ( String msg ) { int rgb = 0; for (int i = 0; i < msg.Length && msg[i] != ':'; i++) { rgb = rgb ^ ((int) msg[i] << (i % 3 + 2) * 8); } Color color = Color.FromArgb(rgb & 0x007F7FFF); richTextBox1.SelectionColor = color; richTextBox1.AppendText(msg + "\n"); } // // OnRegisterReply // // Called by DNSServices core as a result of DNSService.Register() // call // // This is called from a worker thread by DNSService core. // private void OnRegisterReply ( ServiceRef sdRef, ServiceFlags flags, ErrorCode errorCode, String name, String regtype, String domain) { if (errorCode == ErrorCode.NoError) { Invoke(registerServiceCallback, new Object[]{name}); } else { MessageBox.Show("OnRegisterReply returned an error code " + errorCode, "Error"); } } // // OnBrowseReply // // Called by DNSServices core as a result of DNSService.Browse() // call // // This is called from a worker thread by DNSService core. // private void OnBrowseReply ( ServiceRef sdRef, ServiceFlags flags, int interfaceIndex, ErrorCode errorCode, String name, String type, String domain) { if (errorCode == ErrorCode.NoError) { PeerData peer = new PeerData(); peer.InterfaceIndex = interfaceIndex; peer.Name = name; peer.Type = type; peer.Domain = domain; peer.Address = null; if ((flags & ServiceFlags.Add) != 0) { Invoke(addPeerCallback, new Object[]{peer}); } else if ((flags == 0) || ((flags & ServiceFlags.MoreComing) != 0)) { Invoke(removePeerCallback, new Object[]{peer}); } } else { MessageBox.Show("OnBrowseReply returned an error code " + errorCode, "Error"); } } // // OnResolveReply // // Called by DNSServices core as a result of DNSService.Resolve() // call // // This is called from a worker thread by DNSService core. // private void OnResolveReply ( ServiceRef sdRef, ServiceFlags flags, int interfaceIndex, ErrorCode errorCode, String fullName, String hostName, int port, Byte[] txtRecord ) { if (errorCode == ErrorCode.NoError) { ResolveData data = new ResolveData(); data.InterfaceIndex = interfaceIndex; data.FullName = fullName; data.HostName = hostName; data.Port = port; data.TxtRecord = txtRecord; Invoke(resolveServiceCallback, new Object[]{data}); } else { MessageBox.Show("OnResolveReply returned an error code: " + errorCode, "Error"); } } // // OnQueryRecordReply // // Called by DNSServices core as a result of DNSService.QueryRecord() // call // // This is called from a worker thread by DNSService core. // private void OnQueryRecordReply ( ServiceRef sdRef, ServiceFlags flags, int interfaceIndex, ErrorCode errorCode, String fullName, int rrtype, int rrclass, Byte[] rdata, int ttl ) { if (errorCode == ErrorCode.NoError) { uint bits = BitConverter.ToUInt32(rdata, 0); System.Net.IPAddress data = new System.Net.IPAddress(bits); Invoke(resolveAddressCallback, new Object[]{data}); } else { MessageBox.Show("OnQueryRecordReply returned an error code: " + errorCode, "Error"); } } // // OnReadSocket // // Called by the .NET core when there is data to be read on a socket // // This is called from a worker thread by the .NET core // private void OnReadSocket ( IAsyncResult ar ) { SocketStateObject so = (SocketStateObject) ar.AsyncState; Socket s = so.WorkSocket; try { if (s == null) { return; } int read = s.EndReceive(ar); if (read > 0) { String msg = Encoding.UTF8.GetString(so.m_buffer, 0, read); Invoke(readMessageCallback, new Object[]{msg}); } s.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(OnReadSocket), so); } catch { } } public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); registerServiceCallback = new RegisterServiceCallback(OnRegisterService); addPeerCallback = new AddPeerCallback(OnAddPeer); removePeerCallback = new RemovePeerCallback(OnRemovePeer); resolveServiceCallback = new ResolveServiceCallback(OnResolveService); resolveAddressCallback = new ResolveAddressCallback(OnResolveAddress); readMessageCallback = new ReadMessageCallback(OnReadMessage); this.Load += new System.EventHandler(this.Form1_Load); this.AcceptButton = button1; } /// /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { if (components != null) { components.Dispose(); } if (registrar != null) { registrar.Dispose(); } if (browser != null) { browser.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.comboBox1 = new System.Windows.Forms.ComboBox(); this.textBox2 = new System.Windows.Forms.TextBox(); this.button1 = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); this.richTextBox1 = new System.Windows.Forms.RichTextBox(); this.SuspendLayout(); // // comboBox1 // this.comboBox1.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right); this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBox1.Location = new System.Drawing.Point(59, 208); this.comboBox1.Name = "comboBox1"; this.comboBox1.Size = new System.Drawing.Size(224, 21); this.comboBox1.Sorted = true; this.comboBox1.TabIndex = 5; this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); // // textBox2 // this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right); this.textBox2.Location = new System.Drawing.Point(8, 248); this.textBox2.Name = "textBox2"; this.textBox2.ScrollBars = System.Windows.Forms.ScrollBars.Horizontal; this.textBox2.Size = new System.Drawing.Size(192, 20); this.textBox2.TabIndex = 2; this.textBox2.Text = ""; this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged); // // button1 // this.button1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right); this.button1.Enabled = false; this.button1.Location = new System.Drawing.Point(208, 248); this.button1.Name = "button1"; this.button1.TabIndex = 3; this.button1.Text = "Send"; this.button1.Click += new System.EventHandler(this.button1_Click); // // label1 // this.label1.Anchor = (System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left); this.label1.Location = new System.Drawing.Point(8, 210); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(48, 16); this.label1.TabIndex = 4; this.label1.Text = "Talk To:"; // // richTextBox1 // this.richTextBox1.Anchor = (((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right); this.richTextBox1.Location = new System.Drawing.Point(8, 8); this.richTextBox1.Name = "richTextBox1"; this.richTextBox1.ReadOnly = true; this.richTextBox1.Size = new System.Drawing.Size(272, 184); this.richTextBox1.TabIndex = 1; this.richTextBox1.Text = ""; // // Form1 // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(292, 273); this.Controls.AddRange(new System.Windows.Forms.Control[] { this.richTextBox1, this.label1, this.button1, this.textBox2, this.comboBox1}); this.Name = "Form1"; this.Text = "SimpleChat.NET"; this.ResumeLayout(false); } #endregion private void Form1_Load(object sender, EventArgs e) { IPEndPoint localEP = new IPEndPoint(System.Net.IPAddress.Any, 0); // // create the socket and bind to INADDR_ANY // socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); socket.Bind(localEP); localEP = (IPEndPoint) socket.LocalEndPoint; // // start asynchronous read // SocketStateObject so = new SocketStateObject(socket); socket.BeginReceive(so.m_buffer, 0, SocketStateObject.BUFFER_SIZE, 0, new AsyncCallback(this.OnReadSocket), so); try { // // start the register and browse operations // registrar = DNSService.Register(0, 0, System.Environment.UserName, "_p2pchat._udp", null, null, localEP.Port, null, new DNSService.RegisterReply(OnRegisterReply)); browser = DNSService.Browse(0, 0, "_p2pchat._udp", null, new DNSService.BrowseReply(OnBrowseReply)); } catch { MessageBox.Show("DNSServices Not Available", "Error"); Application.Exit(); } } /// /// The main entry point for the application. /// [STAThread] static void Main() { Application.Run(new Form1()); } // // send the message to a peer // private void button1_Click(object sender, System.EventArgs e) { PeerData peer = (PeerData) comboBox1.SelectedItem; String message = myName + ": " + textBox2.Text; Byte[] bytes = Encoding.UTF8.GetBytes(message); UdpClient udpSocket = new UdpClient(peer.Address.ToString(), peer.Port); udpSocket.Send(bytes, bytes.Length); richTextBox1.SelectionColor = Color.Black; richTextBox1.AppendText(textBox2.Text + "\n"); textBox2.Text = ""; } // // called when typing in message box // private void textBox2_TextChanged(object sender, System.EventArgs e) { PeerData peer = (PeerData) comboBox1.SelectedItem; if ((peer.Address != null) && (textBox2.Text.Length > 0)) { button1.Enabled = true; } else { button1.Enabled = false; } } // // called when peer target changes // /// /// /// /// private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e) { PeerData peer = (PeerData) comboBox1.SelectedItem; try { resolver = DNSService.Resolve(0, 0, peer.Name, peer.Type, peer.Domain, new DNSService.ResolveReply(OnResolveReply)); } catch { MessageBox.Show("Unable to Resolve service", "Error"); Application.Exit(); } } } }