用c#开发安卓程序 (xamarin.android)系列之二 简单的聊天程序

networkcomm.net 网络通信框架来自于英国剑桥,其开源版本2.3.1 中自带了一个编写android的例子,可以很好的帮助我们入门。

此示例的功能,是在2个安卓手机上,输入对方的IP和端口,能够实现聊天功能。

把代码放上,供大家一览

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.Net;
using System.Collections.Generic;
using System.IO;

namespace ExamplesChat.Android
{
    [Activity(Label = "ExamplesChat.Android", MainLauncher = true, Icon = "@drawable/icon")]
    public class Activity1 : Activity
    {
        #region Private Fields
        /// <summary>
        /// 一个聊天程序的实例
        /// </summary>
        ChatAppAndroid chatApplication;

        /// <summary>
        /// 用于显示聊天消息
        /// </summary>
        TextView chatHistory;

        /// <summary>
        /// 发送消息按钮
        /// </summary>
        Button sendButton;

        /// <summary>
        /// 消息输入框
        /// </summary>
        AutoCompleteTextView input;

        /// <summary>
        /// The texbox containing the master ip address (server)
        /// </summary>
        AutoCompleteTextView ipTextBox;

        /// <summary>
        /// The texbox containing the master port number (server)
        /// </summary>
        AutoCompleteTextView portTextBox;

        /// <summary>
        /// The spinner (drop down) menu for selecting the connection type to use
        /// </summary>
        Spinner connectionTypeSelector;

        /// <summary>
        /// The checkbox which can be used to enable local server mode
        /// </summary>
        CheckBox enableLocalServerCheckBox;
        #endregion

        /// <summary>
        /// Method runs after the application has been launched
        /// </summary>
        /// <param name="bundle"></param>
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            //Get references to user interface controls
            connectionTypeSelector = FindViewById<Spinner>(Resource.Id.connectionTypeSpinner);
            chatHistory = FindViewById<TextView>(Resource.Id.mainText);
            input = FindViewById<AutoCompleteTextView>(Resource.Id.messageTextInput);
            ipTextBox = FindViewById<AutoCompleteTextView>(Resource.Id.ipTextInput);
            portTextBox = FindViewById<AutoCompleteTextView>(Resource.Id.portTextInput);
            sendButton = FindViewById<Button>(Resource.Id.sendButton);
            enableLocalServerCheckBox = FindViewById<CheckBox>(Resource.Id.enableLocalServer);

            //Set the connection type selection drop down options
            ArrayAdapter adapter = ArrayAdapter.CreateFromResource(this, Resource.Array.ConnectionTypes, global::Android.Resource.Layout.SimpleSpinnerItem);
            adapter.SetDropDownViewResource(global::Android.Resource.Layout.SimpleSpinnerDropDownItem);
            connectionTypeSelector.Adapter = adapter;

            //Append the method ‘connectionType_Selected‘ to the connection type selected event
            connectionTypeSelector.ItemSelected += connectionType_Selected;

            //Append the method ‘sendButton_Click‘ to the button click event
            sendButton.Click += sendButton_Click;

            //Append the method ‘enableLocalServerCheckBox_CheckedChange‘ when the enable
            //local server checkbox state is changed
            enableLocalServerCheckBox.CheckedChange += enableLocalServerCheckBox_CheckedChange;

            //Initialise the chat application
            chatApplication = new ChatAppAndroid(this, chatHistory, input);

            //Print the usage instructions
            chatApplication.PrintUsageInstructions();

            //Initialise NetworkComms.Net but without a local server
            chatApplication.RefreshNetworkCommsConfiguration();

            //Uncomment this line to enable logging
            //EnableLogging();
        }

        /// <summary>
        /// Enable NetworkComms.Net logging. Usefull for debugging.
        /// </summary>
        void EnableLogging()
        {
            //We will create the log file in the root external storage directory
            string sdCardDir = global::Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
            string logFileName = Path.Combine(sdCardDir, "NetworkCommsLog.txt");

            chatApplication.AppendLineToChatHistory(System.Environment.NewLine + "Logging enabled to " + logFileName);

            NetworkCommsDotNet.NetworkComms.EnableLogging(logFileName);
        }

        #region Event Handlers
        /// <summary>
        /// Event triggered when the enable local server checkbox is changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void enableLocalServerCheckBox_CheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e)
        {
            //Update the local server enabled state and then refresh the network configuration
            chatApplication.LocalServerEnabled = enableLocalServerCheckBox.Checked;
            chatApplication.RefreshNetworkCommsConfiguration();
        }

        /// <summary>
        /// Event triggered when the send button is clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void sendButton_Click(object sender, EventArgs e)
        {
            //Parse the ip address box
            if (ipTextBox.Text != "")
            {
                IPAddress newMasterIPAddress;
                if (IPAddress.TryParse(ipTextBox.Text, out newMasterIPAddress))
                    //If the parse was succesfull we can update the chat application
                    chatApplication.ServerIPAddress = newMasterIPAddress.ToString();
                else
                    //If the parse failed set the ipTextBox back to the the previous good value
                    ipTextBox.Text = chatApplication.ServerIPAddress;
            }
            else
                //If no server IP has been entered we ensure the chat application has a blank address
                chatApplication.ServerIPAddress = "";

            //Parse the port number
            if (portTextBox.Text != "")
            {
                int newPort;
                bool portParseResult = int.TryParse(portTextBox.Text, out newPort);
                if (!portParseResult || newPort < 1 || newPort > ushort.MaxValue)
                    //If the parse failed we set the portTextBox back to the previous good value
                    portTextBox.Text = chatApplication.ServerPort.ToString();
                else
                    chatApplication.ServerPort = newPort;
            }
            else
                chatApplication.ServerPort = -1;

            //Send the text entered in the input box
            chatApplication.SendMessage(input.Text);
        }

        /// <summary>
        /// Checks if the selected connection type has changed. If changed reset the example to use the new connection type.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void connectionType_Selected(object sender, EventArgs e)
        {
            //Parse the connection type
            string selectedItem = connectionTypeSelector.SelectedItem.ToString();
            if (selectedItem == "TCP")
                chatApplication.ConnectionType = NetworkCommsDotNet.ConnectionType.TCP;
            else if (selectedItem == "UDP")
                chatApplication.ConnectionType = NetworkCommsDotNet.ConnectionType.UDP;

            //Update the NetworkComms.Net configuration
            chatApplication.RefreshNetworkCommsConfiguration();
        }
        #endregion
    }
}

MainActivity

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;

namespace ExamplesChat.Android
{
    /// <summary>
    /// All NetworkComms.Net implementation can be found here and in ChatAppBase
    /// </summary>
    public class ChatAppAndroid : ChatAppBase
    {
        #region Public Fields
        /// <summary>
        /// The chat history window. This is where all of the message appear
        /// </summary>
        public TextView ChatHistory { get; private set; }

        /// <summary>
        /// The input box where new messages are input
        /// </summary>
        public AutoCompleteTextView Input { get; private set; }

        /// <summary>
        /// The parent context of this object
        /// </summary>
        public Context ParentContext { get; private set; }
        #endregion

        #region Private Fields
        /// <summary>
        /// Handler used to post information to the parent context
        /// </summary>
        Handler handler = new Handler();
        #endregion

        /// <summary>
        /// Constructor for the Android chat app.
        /// </summary>
        public ChatAppAndroid(Context parentContext, TextView chatHistory, AutoCompleteTextView input)
            : base("Android", NetworkCommsDotNet.ConnectionType.TCP)
        {
            this.ParentContext = parentContext;
            this.ChatHistory = chatHistory;
            this.Input = input;
        }

        #region GUI Interface Overrides
        /// <summary>
        /// Append the provided message to the chatBox text box.
        /// </summary>
        /// <param name="message">Message to be appended</param>
        public override void AppendLineToChatHistory(string message)
        {
            handler.Post(() => { ChatHistory.Text += System.Environment.NewLine + message; });
        }

        /// <summary>
        /// Clear all previous chat history
        /// </summary>
        /// <param name="message">Message to be appended</param>
        public override void ClearChatHistory()
        {
            handler.Post(() => { ChatHistory.Text = ""; });
        }

        /// <summary>
        /// Clears the input text box
        /// </summary>
        public override void ClearInputLine()
        {
            handler.Post(() => { Input.Text = ""; });
        }

        /// <summary>
        /// Ouput message on error
        /// </summary>
        /// <param name="message">Message to be output</param>
        public override void ShowMessage(string message)
        {
            handler.Post(() =>
                {
                    AlertDialog dialog = (new AlertDialog.Builder(ParentContext)).Create();
                    dialog.SetCancelable(false); // This blocks the ‘BACK‘ button
                    dialog.SetMessage(message);
                    dialog.SetButton("OK", new EventHandler<DialogClickEventArgs>((obj, args) =>
                    {
                        dialog.Dismiss();
                    }));

                    dialog.Show();
                });
        }
        #endregion
    }
}

ChatAppAndroid

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using NetworkCommsDotNet;
using DPSBase;

namespace ExamplesChat.Android
{
    /// <summary>
    /// In an attempt to keep things as clear as possible all shared implementation across chat examples
    /// has been provided in this base class.
    /// </summary>
    public abstract class ChatAppBase
    {
        #region Private Fields
        /// <summary>
        /// A boolean used to track the very first initialisation
        /// </summary>
        protected bool FirstInitialisation { get; set; }

        /// <summary>
        /// Dictionary to keep track of which peer messages have already been written to the chat window
        /// </summary>
        protected Dictionary<ShortGuid, ChatMessage> lastPeerMessageDict = new Dictionary<ShortGuid, ChatMessage>();

        /// <summary>
        /// The maximum number of times a chat message will be relayed
        /// </summary>
        int relayMaximum = 3;

        /// <summary>
        /// A local counter used to track the number of messages sent from
        /// this instance.
        /// </summary>
        long messageSendIndex = 0;

        /// <summary>
        /// An optional encryption key to use should one be required.
        /// This can be changed freely but must obviously be the same
        /// for both sender and reciever.
        /// </summary>
        string _encryptionKey = "ljlhjf8uyfln23490jf;m21-=scm20--iflmk;";
        #endregion

        #region Public Fields
        /// <summary>
        /// The type of connection currently used to send and recieve messages. Default is TCP.
        /// </summary>
        public ConnectionType ConnectionType { get; set; }

        /// <summary>
        /// The IP address of the server
        /// </summary>
        public string ServerIPAddress { get; set; }

        /// <summary>
        /// The port of the server
        /// </summary>
        public int ServerPort { get; set; }

        /// <summary>
        /// The local name used when sending messages
        /// </summary>
        public string LocalName { get; set; }

        /// <summary>
        /// A boolean used to track if the local device is acting as a server
        /// </summary>
        public bool LocalServerEnabled { get; set; }

        /// <summary>
        /// A boolean used to track if encryption is currently being used
        /// </summary>
        public bool EncryptionEnabled { get; set; }
        #endregion

        /// <summary>
        /// Constructor for ChatAppBase
        /// </summary>
        public ChatAppBase(string name, ConnectionType connectionType)
        {
            LocalName = name;
            ConnectionType = connectionType;

            //Initialise the default values
            ServerIPAddress = "";
            ServerPort = 10000;
            LocalServerEnabled = false;
            EncryptionEnabled = false;
            FirstInitialisation = true;
        }

        #region NetworkComms.Net Methods
        /// <summary>
        /// Updates the configuration of this instance depending on set fields
        /// </summary>
        public void RefreshNetworkCommsConfiguration()
        {
            #region First Initialisation
            //On first initilisation we need to configure NetworkComms.Net to handle our incoming packet types
            //We only need to add the packet handlers once. If we call NetworkComms.Shutdown() at some future point these are not removed.
            if (FirstInitialisation)
            {
                FirstInitialisation = false;

                //Configure NetworkComms.Net to handle any incoming packet of type ‘ChatMessage‘
                //e.g. If we recieve a packet of type ‘ChatMessage‘ execute the method ‘HandleIncomingChatMessage‘
                NetworkComms.AppendGlobalIncomingPacketHandler<ChatMessage>("ChatMessage", HandleIncomingChatMessage);

                //Configure NetworkComms.Net to perform some action when a connection is closed
                //e.g. When a connection is closed execute the method ‘HandleConnectionClosed‘
                NetworkComms.AppendGlobalConnectionCloseHandler(HandleConnectionClosed);
            }
            #endregion

            #region Optional Encryption
            //Configure encryption if requested
            if (EncryptionEnabled && !NetworkComms.DefaultSendReceiveOptions.DataProcessors.Contains(DPSBase.DPSManager.GetDataProcessor<DPSBase.RijndaelPSKEncrypter>()))
            {
                //Encryption is currently implemented using a pre-shared key (PSK) system
                //NetworkComms.Net supports multiple data processors which can be used with any level of granularity
                //To enable encryption globally (i.e. for all connections) we first add the encryption password as an option
                DPSBase.RijndaelPSKEncrypter.AddPasswordToOptions(NetworkComms.DefaultSendReceiveOptions.Options, _encryptionKey);
                //Finally we add the RijndaelPSKEncrypter data processor to the sendReceiveOptions
                NetworkComms.DefaultSendReceiveOptions.DataProcessors.Add(DPSBase.DPSManager.GetDataProcessor<DPSBase.RijndaelPSKEncrypter>());
            }
            else if (!EncryptionEnabled && NetworkComms.DefaultSendReceiveOptions.DataProcessors.Contains(DPSBase.DPSManager.GetDataProcessor<DPSBase.RijndaelPSKEncrypter>()))
            {
                //If encryption has been disabled but is currently enabled
                //To disable encryption we just remove the RijndaelPSKEncrypter data processor from the sendReceiveOptions
                NetworkComms.DefaultSendReceiveOptions.DataProcessors.Remove(DPSBase.DPSManager.GetDataProcessor<DPSBase.RijndaelPSKEncrypter>());
            }
            #endregion

            #region Local Server Mode and Connection Type Changes
            if (LocalServerEnabled && ConnectionType == ConnectionType.TCP && !TCPConnection.Listening())
            {
                //If we were previously listening for UDP we first shutdown comms.
                if (UDPConnection.Listening())
                {
                    AppendLineToChatHistory("Connection mode has been changed. Any existing connections will be closed.");
                    NetworkComms.Shutdown();
                }
                else
                {
                    AppendLineToChatHistory("Enabling local server mode. Any existing connections will be closed.");
                    NetworkComms.Shutdown();
                }

                //Start listening for new incoming TCP connections
                //Parameter is true so that we listen on a random port if the default is not available
                TCPConnection.StartListening(true);

                //Write the IP addresses and ports that we are listening on to the chatBox
                AppendLineToChatHistory("Listening for incoming TCP connections on:");
                foreach (var listenEndPoint in TCPConnection.ExistingLocalListenEndPoints())
                    AppendLineToChatHistory(listenEndPoint.Address + ":" + listenEndPoint.Port);

                //Add a blank line after the initialisation output
                AppendLineToChatHistory(System.Environment.NewLine);
            }
            else if (LocalServerEnabled && ConnectionType == ConnectionType.UDP && !UDPConnection.Listening())
            {
                //If we were previously listening for TCP we first shutdown comms.
                if (TCPConnection.Listening())
                {
                    AppendLineToChatHistory("Connection mode has been changed. Any existing connections will be closed.");
                    NetworkComms.Shutdown();
                }
                else
                {
                    AppendLineToChatHistory("Enabling local server mode. Any existing connections will be closed.");
                    NetworkComms.Shutdown();
                }

                //Start listening for new incoming UDP connections
                //Parameter is true so that we listen on a random port if the default is not available
                UDPConnection.StartListening(true);

                //Write the IP addresses and ports that we are listening on to the chatBox
                AppendLineToChatHistory("Listening for incoming UDP connections on:");
                foreach (var listenEndPoint in UDPConnection.ExistingLocalListenEndPoints())
                    AppendLineToChatHistory(listenEndPoint.Address + ":" + listenEndPoint.Port);

                //Add a blank line after the initialisation output
                AppendLineToChatHistory(System.Environment.NewLine);
            }
            else if (!LocalServerEnabled && (TCPConnection.Listening() || UDPConnection.Listening()))
            {
                //If the local server mode has been disabled but we are still listening we need to stop accepting incoming connections
                NetworkComms.Shutdown();
                AppendLineToChatHistory("Local server mode disabled. Any existing connections will be closed.");
                AppendLineToChatHistory(System.Environment.NewLine);
            }
            else if (!LocalServerEnabled &&
                ((ConnectionType == ConnectionType.UDP && NetworkComms.GetExistingConnection(ConnectionType.TCP).Count > 0) ||
                (ConnectionType == ConnectionType.TCP && NetworkComms.GetExistingConnection(ConnectionType.UDP).Count > 0)))
            {
                //If we are not running a local server but have changed the connection type after creating connections we need to close
                //existing connections.
                NetworkComms.Shutdown();
                AppendLineToChatHistory("Connection mode has been changed. Existing connections will be closed.");
                AppendLineToChatHistory(System.Environment.NewLine);
            }
            #endregion
        }

        /// <summary>
        /// Performs whatever functions we might so desire when we recieve an incoming ChatMessage
        /// </summary>
        /// <param name="header">The PacketHeader corresponding with the recieved object</param>
        /// <param name="connection">The Connection from which this object was recieved</param>
        /// <param name="incomingMessage">The incoming ChatMessage we are after</param>
        protected virtual void HandleIncomingChatMessage(PacketHeader header, Connection connection, ChatMessage incomingMessage)
        {
            //We only want to write a message once to the chat window
            //Because we support relaying and may recieve the same message twice from multiple sources
            //we use our history and message indexes to ensure we have a new message
            //We perform this action within a lock as HandleIncomingChatMessage could be called in parallel
            lock (lastPeerMessageDict)
            {
                if (lastPeerMessageDict.ContainsKey(incomingMessage.SourceIdentifier))
                {
                    if (lastPeerMessageDict[incomingMessage.SourceIdentifier].MessageIndex < incomingMessage.MessageIndex)
                    {
                        //If this message index is greater than the last seen from this source we can safely
                        //write the message to the ChatBox
                        AppendLineToChatHistory(incomingMessage.SourceName + " - " + incomingMessage.Message);

                        //We now replace the last recieved message with the current one
                        lastPeerMessageDict[incomingMessage.SourceIdentifier] = incomingMessage;
                    }
                }
                else
                {
                    //If we have never had a message from this source before then it has to be new
                    //by defintion
                    lastPeerMessageDict.Add(incomingMessage.SourceIdentifier, incomingMessage);
                    AppendLineToChatHistory(incomingMessage.SourceName + " - " + incomingMessage.Message);
                }
            }

            //This last section of the method is the relay feature
            //We start by checking to see if this message has already been relayed the maximum number of times
            if (incomingMessage.RelayCount < relayMaximum)
            {
                //If we are going to relay this message we need an array of
                //all known connections, excluding the current one
                var allRelayConnections = (from current in NetworkComms.GetExistingConnection() where current != connection select current).ToArray();

                //We increment the relay count before we send
                incomingMessage.IncrementRelayCount();

                //We now send the message to every other connection
                foreach (var relayConnection in allRelayConnections)
                {
                    //We ensure we perform the send within a try catch
                    //To ensure a single failed send will not prevent the
                    //relay to all working connections.
                    try { relayConnection.SendObject("ChatMessage", incomingMessage); }
                    catch (CommsException) { /* Catch the comms exception, ignore and continue */ }
                }
            }
        }

        /// <summary>
        /// Performs whatever functions we might so desire when an existing connection is closed.
        /// </summary>
        /// <param name="connection">The closed connection</param>
        private void HandleConnectionClosed(Connection connection)
        {
            //We are going to write a message to the chat history when a connection disconnects
            //We perform the following within a lock incase mutliple connections disconnect simultaneously
            lock (lastPeerMessageDict)
            {
                //Get the remoteIdentifier from the closed connection
                //This a unique GUID which can be used to identify peers
                ShortGuid remoteIdentifier = connection.ConnectionInfo.NetworkIdentifier;

                //If at any point we recieved a message with a matching identifier we can
                //include the peer name in the disconnection message.
                if (lastPeerMessageDict.ContainsKey(remoteIdentifier))
                    AppendLineToChatHistory("Connection with ‘" + lastPeerMessageDict[remoteIdentifier].SourceName + "‘ has been closed.");
                else
                    AppendLineToChatHistory("Connection with ‘" + connection.ToString() + "‘ has been closed.");

                //Last thing is to remove this peer from our message history
                lastPeerMessageDict.Remove(connection.ConnectionInfo.NetworkIdentifier);
            }
        }

        /// <summary>
        /// Send a message.
        /// </summary>
        public void SendMessage(string stringToSend)
        {
            //If we have tried to send a zero length string we just return
            if (stringToSend.Trim() == "") return;

            //We may or may not have entered some server connection information
            ConnectionInfo serverConnectionInfo = null;
            if (ServerIPAddress != "")
            {
                try { serverConnectionInfo = new ConnectionInfo(ServerIPAddress, ServerPort); }
                catch (Exception)
                {
                    ShowMessage("Failed to parse the server IP and port. Please ensure it is correct and try again");
                    return;
                }
            }

            //We wrap everything we want to send in the ChatMessage class we created
            ChatMessage chatMessage = new ChatMessage(NetworkComms.NetworkIdentifier, LocalName, stringToSend, messageSendIndex++);

            //We add our own message to the message history incase it gets relayed back to us
            lock (lastPeerMessageDict) lastPeerMessageDict[NetworkComms.NetworkIdentifier] = chatMessage;

            //We write our own message to the chatBox
            AppendLineToChatHistory(chatMessage.SourceName + " - " + chatMessage.Message);

            //Clear the input box text
            ClearInputLine();

            //If we provided server information we send to the server first
            if (serverConnectionInfo != null)
            {
                //We perform the send within a try catch to ensure the application continues to run if there is a problem.
                try
                {
                    if (ConnectionType == ConnectionType.TCP)
                        TCPConnection.GetConnection(serverConnectionInfo).SendObject("ChatMessage", chatMessage);
                    else if (ConnectionType == ConnectionType.UDP)
                        UDPConnection.GetConnection(serverConnectionInfo, UDPOptions.None).SendObject("ChatMessage", chatMessage);
                    else
                        throw new Exception("An invalid connectionType is set.");
                }
                catch (CommsException) { AppendLineToChatHistory("Error: A communication error occured while trying to send message to " + serverConnectionInfo + ". Please check settings and try again."); }
                catch (Exception) { AppendLineToChatHistory("Error: A general error occured while trying to send message to " + serverConnectionInfo + ". Please check settings and try again."); }
            }

            //If we have any other connections we now send the message to those as well
            //This ensures that if we are the server everyone who is connected to us gets our message
            //We want a list of all established connections not including the server if set
            List<ConnectionInfo> otherConnectionInfos;
            if (serverConnectionInfo != null)
                otherConnectionInfos = (from current in NetworkComms.AllConnectionInfo() where current.RemoteEndPoint != serverConnectionInfo.RemoteEndPoint select current).ToList();
            else
                otherConnectionInfos = NetworkComms.AllConnectionInfo();

            foreach (ConnectionInfo info in otherConnectionInfos)
            {
                //We perform the send within a try catch to ensure the application continues to run if there is a problem.
                try
                {
                    if (ConnectionType == ConnectionType.TCP)
                        TCPConnection.GetConnection(info).SendObject("ChatMessage", chatMessage);
                    else if (ConnectionType == ConnectionType.UDP)
                        UDPConnection.GetConnection(info, UDPOptions.None).SendObject("ChatMessage", chatMessage);
                    else
                        throw new Exception("An invalid connectionType is set.");
                }
                catch (CommsException) { AppendLineToChatHistory("Error: A communication error occured while trying to send message to " + info + ". Please check settings and try again."); }
                catch (Exception) { AppendLineToChatHistory("Error: A general error occured while trying to send message to " + info + ". Please check settings and try again."); }
            }

            return;
        }
        #endregion

        #region GUI Interface Methods
        /// <summary>
        /// Outputs the usage instructions to the chat window
        /// </summary>
        public void PrintUsageInstructions()
        {
            AppendLineToChatHistory("");
            AppendLineToChatHistory("Chat usage instructions:");
            AppendLineToChatHistory("");
            AppendLineToChatHistory("Step 1. Open atleast two chat applications. You can choose from Android, Windows Phone, iOS or native Windows versions.");
            AppendLineToChatHistory("Step 2. Enable local server mode in a single application, see settings.");
            AppendLineToChatHistory("Step 3. Provide remote server IP and port information in settings on remaining application.");
            AppendLineToChatHistory("Step 4. Start chatting.");
            AppendLineToChatHistory("");
            AppendLineToChatHistory("Note: Connections are established on the first message send.");
            AppendLineToChatHistory("");
        }

        /// <summary>
        /// Append the provided message to the chat history text box.
        /// </summary>
        /// <param name="message">Message to be appended</param>
        public abstract void AppendLineToChatHistory(string message);

        /// <summary>
        /// Clears the chat history
        /// </summary>
        public abstract void ClearChatHistory();

        /// <summary>
        /// Clears the input text box
        /// </summary>
        public abstract void ClearInputLine();

        /// <summary>
        /// Show a message box as an alternative to writing to the chat history
        /// </summary>
        /// <param name="message">Message to be output</param>
        public abstract void ShowMessage(string message);
        #endregion
    }
}

ChatAppBase

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using ProtoBuf;
using NetworkCommsDotNet;

namespace ExamplesChat.Android
{
    /// <summary>
    /// A wrapper class for the messages that we intend to send and recieve.
    /// The [ProtoContract] attribute informs NetworkCommsDotNet that we intend to
    /// serialise (turn into bytes) this object. At the base level the
    /// serialisation is performed by protobuf.net.
    /// </summary>
    [ProtoContract]
    public class ChatMessage
    {
        /// <summary>
        /// The source identifier of this ChatMessage.
        /// We use this variable as the constructor for the ShortGuid.
        /// The [ProtoMember(1)] attribute informs the serialiser that when
        /// an object of type ChatMessage is serialised we want to include this variable
        /// </summary>
        [ProtoMember(1)]
        string _sourceIdentifier;

        /// <summary>
        /// The source identifier is accessible as a ShortGuid
        /// </summary>
        public ShortGuid SourceIdentifier { get { return new ShortGuid(_sourceIdentifier); } }

        /// <summary>
        /// The name of the source of this ChatMessage.
        /// We use shorthand declaration, get and set.
        /// The [ProtoMember(2)] attribute informs the serialiser that when
        /// an object of type ChatMessage is serialised we want to include this variable
        /// </summary>
        [ProtoMember(2)]
        public string SourceName { get; private set; }

        /// <summary>
        /// The actual message.
        /// </summary>
        [ProtoMember(3)]
        public string Message { get; private set; }

        /// <summary>
        /// The index of this message. Every message sent by a particular source
        /// has an incrementing index.
        /// </summary>
        [ProtoMember(4)]
        public long MessageIndex { get; private set; }

        /// <summary>
        /// The number of times this message has been relayed.
        /// </summary>
        [ProtoMember(5)]
        public int RelayCount { get; private set; }

        /// <summary>
        /// We must include a private constructor to be used by the deserialisation step.
        /// </summary>
        public ChatMessage() { }

        /// <summary>
        /// Create a new ChatMessage
        /// </summary>
        /// <param name="sourceIdentifier">The source identifier</param>
        /// <param name="sourceName">The source name</param>
        /// <param name="message">The message to be sent</param>
        /// <param name="messageIndex">The index of this message</param>
        public ChatMessage(ShortGuid sourceIdentifier, string sourceName, string message, long messageIndex)
        {
            this._sourceIdentifier = sourceIdentifier;
            this.SourceName = sourceName;
            this.Message = message;
            this.MessageIndex = messageIndex;
            this.RelayCount = 0;
        }

        /// <summary>
        /// Increment the relay count variable
        /// </summary>
        public void IncrementRelayCount()
        {
            RelayCount++;
        }
    }
}

ChatMessage

最后生成的APK文件

工程文件

www.networkcomms.cn

时间: 2024-10-26 07:02:52

用c#开发安卓程序 (xamarin.android)系列之二 简单的聊天程序的相关文章

C#编写简单的聊天程序(转)

这是一篇基于Socket进行网络编程的入门文章,我对于网络编程的学习并不够深入,这篇文章是对于自己知识的一个巩固,同时希望能为初学的朋友提供一点参考.文章大体分为四个部分:程序的分析与设计.C#网络编程基础(篇外篇).聊天程序的实现模式.程序实现. 程序的分析与设计 1.明确程序功能 如果大家现在已经参加了工作,你的经理或者老板告诉你,“小王,我需要你开发一个聊天程序”.那么接下来该怎么做呢?你是不是在脑子里有个雏形,然后就直接打开VS2005开始设计窗体,编写代码了呢?在开始之前,我们首先需要

isual Studio跨平台开发实战(4) - Xamarin Android基本控制项介绍

原文 isual Studio跨平台开发实战(4) - Xamarin Android基本控制项介绍 前言 不同于iOS, Xamarin 在Visual Studio中针对Android, 可以直接设计使用者介面. 在本篇教学文章中, 笔者会针对Android的专案目录结构以及基本控制项进行介绍, 包含TextView, EditView, Toggle/ Switch以及Seekbar控制项. Android专案目录结构 在Visual Studio建立Android 应用程式专案后, ??

Visual Studio跨平台开发实战(5) - Xamarin Android多页面应用程式开发

原文 Visual Studio跨平台开发实战(5) - Xamarin Android多页面应用程式开发 前言 大部份的Andr??oid 都具有实体或虚拟的Back键. 因此在处理多页面应用程式时, 与先前所介绍的iOS Navigation controller 比较起来会简单许多. 1. 开启Visual Studio 并新增Android Application 专案并命名为Lab4-MultiScreen   2. 在Layout资料夹中新增Second.axml   在Second

posix 线程(一):线程模型、pthread 系列函数 和 简单多线程服务器端程序

posix 线程(一):线程模型.pthread 系列函数 和 简单多线程服务器端程序 一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属于1:1模型. (一).N:1用户线程模型 “线程实现”建立在“进程控制”机制之上,由用户空间的程序库来管理.OS内核完全不知道线程信息.这些线程称为用户空间线程.这些线程都工作在“进 程竞争范围”(process contention scope):各个线程在同一进程竞争“被调度的CPU时间”(但

Xamarin.Android之封装个简单的网络请求类

一.前言 回忆到上篇 <Xamarin.Android再体验之简单的登录Demo> 做登录时,用的是GET的请求,还用的是同步, 于是现在将其简单的改写,做了个简单的封装,包含基于HttpClient和HttpWebRequest两种方式的封装. 由于对这一块还不是很熟悉,所以可能不是很严谨. 二.先上封装好的代码 1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Json

从一个简单的聊天程序SimpleChat看VPN技术

SimpleVPN写好了以后,感觉比较简单,我觉得只有简单的东西才经得起折腾,才能全民折腾,所以说SimpleVPN还不够简单,本文来一个更加简单的,展示一个超级简单的点对点聊天程序,而且还带简单加密哦.顺便,我们再来看下,到底什么是VPN以及怎样实现它.       QQ如今才刚刚行过成年之礼,典型的90后00前,却早已到了后浪把前浪拍到岸边的砍儿,果不其然,被10后的微信给逆袭了...好在都是腾讯的,这就把竞争收敛到了公司内部,不然这将意味着一个巨人的倒下,太可怕了.多年前,很多人逆向过QQ

基于Java实现hello/hi简单网络聊天程序

目录 Socket简要阐述 Socket的概念 Socket原理 hello/hi的简单网络聊天程序实现 服务器端 客户端 程序执行结果 跟踪分析调用栈 & Linux API对比 创建ServerSocket 调用栈图示 源码分析 Socket绑定 调用栈图示 源码分析 Socket监听 调用栈图示 源码分析 Socket Accept 调用栈图示 源码分析 Java Socekt API与Linux Socket API 参考链接 Socket简要阐述 Socket的概念 Socket的英文

使用Ajax long polling实现简单的聊天程序

关于web实时通信,通常使用长轮询或这长连接方式进行实现. 为了能够实际体会长轮询,通过Ajax长轮询实现了一个简单的聊天程序,在此作为笔记. 长轮询 传统的轮询方式是,客户端定时(一般使用setInterval)向服务器发送Ajax请求,服务器接到请求后马上返回响应信息.使用这种方式,无论客户端还是服务端都比较好实现,但是会有很多无用的请求(服务器没有有效数据的时候,也需要返回通知客户端). 而长轮询是,客户端向服务器发送Ajax请求,服务器接到请求后保持住连接,直到有新消息才返回响应信息,客

Xamarin.Android之Spinner的简单探讨

一.前言 今天用了一下Spinner这个控件,主要是结合官网的例子来用的,不过官网的是把数据写在Strings.xml中的, 某种程度上,不是很符合我们需要的,比较多的应该都是从数据库读出来,绑定上去的.下面是官网的例子: https://developer.xamarin.com/guides/android/user_interface/spinner/ 二.来个简单的Demo 新建一个类 MyType.cs 1 public class MyType 2 { 3 public int Ty