C# Socket实现聊天通信

本项目演示了如何使用 C# 和 Socket 实现一个简单的局域网聊天室应用,包括服务端和客户端代码。服务端负责消息的转发,而客户端能够连接服务端并与其他客户端进行实时通信。

C# Socket实现聊天通信

下面这个实例是一个完整的使用Socket实现的聊天(只限于局域网,如果能提供一个高权限的IP就可以实现类似QQ聊天),其中的原理是:首先开启服务端,打开侦听(任何端口为6600的IP),下面实现的代码:服务端+客户端【VS2005 C#.NET 2.0】

【服务端】{三个窗体:About.cs,ServerMain.cs,Set.cs}:

(ServerMain.cs窗体代码)

using System;

using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Xml;

namespace Server
{
    public partial class ServerMain : Form
    {
        public ServerMain()
        {
            InitializeComponent();
        }

        private void ServerMain_Load(object sender, EventArgs e)
        {
            this.CmdStar.Enabled = true;
            this.CmdStop.Enabled = false;
        }

        private void 配置参数ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Set TSet = new Set();
            TSet.ShowDialog();
        }

        private void 关于ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            About TAbout = new About();
            TAbout.Show();
        }
        /// <summary>
        /// 获得XML文件中的端口号
        /// </summary>
        /// <returns></returns>
        private int GetPort()
        {
            try
            {
                XmlDocument TDoc = new XmlDocument();
                TDoc.Load("Settings.xml");
                string TPort = TDoc.GetElementsByTagName("ServerPort")[0].InnerXml;
                return Convert.ToInt32(TPort);

            }
            catch { return 6600; }//默认是6600
        }

        //声明将要用到的类
        private IPEndPoint ServerInfo;//存放服务器的IP和端口信息
        private Socket ServerSocket;//服务端运行的SOCKET
        private Thread ServerThread;//服务端运行的线程
        private Socket[] ClientSocket;//为客户端建立的SOCKET连接
        private int ClientNumb;//存放客户端数量
        private byte[] MsgBuffer;//存放消息数据

        private void CmdStar_Click(object sender, EventArgs e)
        {
            ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //提供一个 IP 地址,指示服务器应侦听所有网络接口上的客户端活动
            IPAddress ip = IPAddress.Any;
            ServerInfo=new IPEndPoint(ip,this.GetPort());
            ServerSocket.Bind(ServerInfo);//将SOCKET接口和IP端口绑定
            ServerSocket.Listen(10);//开始监听,并且挂起数为10

            ClientSocket = new Socket[65535];//为客户端提供连接个数
            MsgBuffer = new byte[65535];//消息数据大小
            ClientNumb = 0;//数量从0开始统计

            ServerThread = new Thread(new ThreadStart(RecieveAccept));//将接受客户端连接的方法委托给线程
            ServerThread.Start();//线程开始运行

            CheckForIllegalCrossThreadCalls = false;//不捕获对错误线程的调用

            this.CmdStar.Enabled = false;
            this.CmdStop.Enabled = true;
            this.StateMsg.Text = "服务正在运行..."+" 运行端口:"+this.GetPort().ToString();
            this.ClientList.Items.Add("服务于 " + DateTime.Now.ToString() + " 开始运行.");
        }
        
        //接受客户端连接的方法
        private void RecieveAccept()
        {
            while (true)
            {
                //Accept 以同步方式从侦听套接字的连接请求队列中提取第一个挂起的连接请求,然后创建并返回新的 Socket。
                //在阻止模式中,Accept 将一直处于阻止状态,直到传入的连接尝试排入队列。连接被接受后,原来的 Socket 继续将传入的连接请求排入队列,直到您关闭它。
                ClientSocket[ClientNumb] = ServerSocket.Accept();
                ClientSocket[ClientNumb].BeginReceive(MsgBuffer, 0, MsgBuffer.Length, SocketFlags.None,
                    new AsyncCallback(RecieveCallBack),ClientSocket[ClientNumb]);
                lock (this.ClientList)
                {
                    this.ClientList.Items.Add(ClientSocket[ClientNumb].RemoteEndPoint.ToString() + " 成功连接服务器.");
                }
                ClientNumb++;
            }
        }

        //回发数据给客户端
        private void RecieveCallBack(IAsyncResult AR)
        {
            try
            {
                Socket RSocket = (Socket)AR.AsyncState;
                int REnd = RSocket.EndReceive(AR);
                //对每一个侦听的客户端端口信息进行接收和回发
                for (int i = 0; i < ClientNumb; i++)
                {
                    if (ClientSocket[i].Connected)
                    {
                        //回发数据到客户端
                        ClientSocket[i].Send(MsgBuffer, 0, REnd,SocketFlags.None);
                    }
                    //同时接收客户端回发的数据,用于回发
                    RSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(RecieveCallBack), RSocket);                 
                }
            }
            catch { }

        }

        private void CmdStop_Click(object sender, EventArgs e)
        {
            ServerThread.Abort();//线程终止
            ServerSocket.Close();//关闭socket

            this.CmdStar.Enabled = true;
            this.CmdStop.Enabled = false;
            this.StateMsg.Text = "等待运行...";
            this.ClientList.Items.Add("服务于 " + DateTime.Now.ToString() + " 停止运行.");
        }

        private void ServerMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            ServerThread.Abort();//线程终止
            ServerSocket.Close();//关闭SOCKET
            Application.Exit();
        }
    }
}

Set.cs代码

using System;

using System.Text;
using System.Windows.Forms;

using System.Xml;

namespace Server
{
    public partial class Set : Form
    {
        public Set()
        {
            InitializeComponent();
        }

        private void Set_Load(object sender, EventArgs e)
        {
            this.GetAllIP();
            this.GetPort();
        }

        private void GetAllIP()
        {

        }

        private void GetPort()
        {
            try
            {
                XmlDocument TDoc = new XmlDocument();
                TDoc.Load("Settings.xml");
                string TPort = TDoc.GetElementsByTagName("ServerPort")[0].InnerXml;
                this.Port.Text = TPort;

            }
            catch { }
        }

        private void CmdSave_Click(object sender, EventArgs e)
        {
            try
            {
                XmlDocument TDoc = new XmlDocument();
                TDoc.Load("Settings.xml");

                XmlElement Root = TDoc.DocumentElement;
                XmlElement newElem = TDoc.CreateElement("ServerPort");
                newElem.InnerXml = this.Port.Text;

                Root.ReplaceChild(newElem, Root.LastChild);

                TDoc.Save("Settings.xml");

                MessageBox.Show("参数保存成功!");
                this.Close();
            }
            catch
            {
                MessageBox.Show("参数写入XML文件不成功!");
            }

        }
    }
}

【客户端代码】{ClientMain.cs窗体}

using System;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.Sockets;

namespace Client
{
    public partial class ClientMain : Form
    {
        public ClientMain()
        {
            InitializeComponent();
        }

        private IPEndPoint ServerInfo;
        private Socket ClientSocket;
        //信息接收缓存
        private Byte[] MsgBuffer;
        //信息发送存储
        private Byte[] MsgSend;

        private void ClientMain_Load(object sender, EventArgs e)
        {
            this.CmdSend.Enabled = false;
            this.CmdExit.Enabled = false;
            //定义一个IPV4,TCP模式的Socket
            ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            MsgBuffer = new Byte[65535];
            MsgSend = new Byte[65535];
            //允许子线程刷新数据
            CheckForIllegalCrossThreadCalls = false;
            this.UserName.Text =Environment.MachineName;
        }

        private void CmdEnter_Click(object sender, EventArgs e)
        {
            //服务端IP和端口信息设定,这里的IP可以是127.0.0.1,可以是本机局域网IP,也可以是本机网络IP
            ServerInfo = new IPEndPoint(IPAddress.Parse(this.ServerIP.Text), Convert.ToInt32(this.ServerPort.Text));

            try
            {
                //客户端连接服务端指定IP端口,Sockket
                ClientSocket.Connect(ServerInfo);
                //将用户登录信息发送至服务器,由此可以让其他客户端获知
                ClientSocket.Send(Encoding.Unicode.GetBytes("用户: " + this.UserName.Text + " 进入系统!\n"));
                //开始从连接的Socket异步读取数据。接收来自服务器,其他客户端转发来的信息
                //AsyncCallback引用在异步操作完成时调用的回调方法
                ClientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), null);

                this.SysMsg.Text += "登录服务器成功!\n";
                this.CmdSend.Enabled = true;
                this.CmdEnter.Enabled = false;
                this.CmdExit.Enabled = true;
            }
            catch
            {
                MessageBox.Show("登录服务器失败,请确认服务器是否正常工作!");
            }
        }

        private void ReceiveCallBack(IAsyncResult AR)
        {
            try
            {
                //结束挂起的异步读取,返回接收到的字节数。 AR,它存储此异步操作的状态信息以及所有用户定义数据
                int REnd = ClientSocket.EndReceive(AR);

                lock (this.RecieveMsg)
                {
                    this.RecieveMsg.AppendText(Encoding.Unicode.GetString(MsgBuffer, 0, REnd));
                }
                ClientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallBack), null);

            }
            catch
            {
                MessageBox.Show("已经与服务器断开连接!");
                this.Close();
            }

        }
        //点击发送之后没有直接添加到信息列表中,而是传到服务器,由服务器转发给每个客户端
        private void CmdSend_Click(object sender, EventArgs e)
        {
           
            MsgSend = Encoding.Unicode.GetBytes(this.UserName.Text + "说:\n" + this.SendMsg.Text + "\n");
            if (ClientSocket.Connected)
            {
                //将数据发送到连接的 System.Net.Sockets.Socket。
                ClientSocket.Send(MsgSend);
                this.SendMsg.Text = "";
              
            }
            else
            {
                MessageBox.Show("当前与服务器断开连接,无法发送信息!");
            }
        }

        private void CmdExit_Click(object sender, EventArgs e)
        {
            if (ClientSocket.Connected)
            {
                ClientSocket.Send(Encoding.Unicode.GetBytes(this.UserName.Text + "离开了房间!\n"));
                //禁用发送和接受
                ClientSocket.Shutdown(SocketShutdown.Both);
                //关闭套接字,不允许重用
                ClientSocket.Disconnect(false);
            }
            ClientSocket.Close();

            this.CmdSend.Enabled = false;
            this.CmdEnter.Enabled = true;
            this.CmdExit.Enabled = false;
        }

        private void RecieveMsg_TextChanged(object sender, EventArgs e)
        {
            this.RecieveMsg.ScrollToCaret();
        }

        private void SendMsg_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && e.KeyValue == 13)
            {
                e.Handled = true;
                this.CmdSend_Click(this, null);             
            }
        }
    }
}

下面是该项目的效果图:

1.项目结构图:

 

 

服务端:

客户端效果如下:

1。

 

2。


转载于:https://www.cnblogs.com/8090sns/archive/2012/07/17/Socket.html

一共包括10个以上的聊天程序版本!绝对物有所值! 为感谢大家长期的支持,我将下载所需的资源分下调为2。网络聊天程序设计(可选)  实验要求 1、分析典型网络聊天应用软件(如QQ、MSN等)的实现原理,模拟设计一套网络聊天应用程序,必须实现以下功能: ①按照C/S结构分别设计服务端程序和客户端程序; ②服务端通过图形用户界面实现对服务的控制,负责维护用户帐户和用户群,并维护用户信息、维持客户端之间的端对端通信和群聊通信、适时维护用户在线信息,并能够发送广播消息。 2、增加尽可能多的功能,用户界面友好,操作简便,代码设计遵从程序设计规范,易读性强,对关键过程和代码进行标注说明。 3、程序设计过程遵从软件工程规范,有需求分析、系统设计和详细设计过程,有相应的规范化说明文档。  实验提示 1、客户端之间的通信是通过服务进行转发的,对于两个客户端,服务需要创建两个套接字分别维持与客户端之间的连接。当客户端需要向另一个客户发送消息时,它首先将消息发送到服务,由服务根据目标用户帐户转发到目标主机。 2、群聊是采用多播技术实现的,也可以采用单播技术实现,但是服务开销会增加。具体说来,若采用组播技术,当服务端收到来自一个客户端的消息后,向预先分配的该组组播地址转发该消息。若采用单播技术,服务端需要向该组内的所有客户端一一转发该消息。 3、广播消息通过广播方式发送由服务端创建的消息。 4、服务端根据客户的连接和断开情况,实时向其它客户端发送用户在线信息。 实验题目二:自选网络通信程序设计(可选)  实验要求 可以自选与网络通信相关的设计题目,要求如下: 1、在确定实验题目、设计内容以及设计功能指标要求后,向实验指导教师提交书面申请,由实验指导教师根据所选实验题目的难度和工作量确定立题后方能开始实验。 2、选择的实验题目必须具有一定综合性,并能够利用网络通信原理加以解决,同时需要具备一定的工作量。 3、设计的结果要求用户界面友好,操作简便,代码设计遵从程序设计规范,易读性强,对关键过程和代码进行标注说明。 4、程序设计过程遵从软件工程规范,有需求分析、系统设计和详细设计过程,有相应的规范化说明文档。 5、严禁抄袭别人成果,但可以部分借鉴。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值