黑马程序员-简单聊天室

---------------------- Windows Phone 7手机开发、Net培训、期待与您交流! ----------------------

socket的英文原义是“孔”或“插座”。作为进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄.(其实就是两个程序通信用的)
http使用80端口 ftp使用21端口 smtp使用23端口
socket有两种类型:
流式socket(stream)
是一种面向连接的socket,针对于面向连接的TCP服务应用,安全,但是效率低。
数据报式socket(datagram)
是一种无连接的socket,对应于无连接的UDP服务应用,不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高。
Socket一般应用模式(服务器端和客服端)
1.服务端welcoming socket 开始监听端口(负责监听客服端连接信息)
2.客服端client socket连接服务端指定端口(负责接收和发送服务端消息)
3.服务端welcoming socket监听到客户端连接,创建connection socket(负责和客服端通信)
Socketde的通讯过程
服务器端:
申请一个socket
绑定到一个IP地址和一个端口上
开启侦听,等待接受连接
客户端:
申请一个socket
连接服务器(指明IP地址和端口号)
服务器端接到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并 进行通讯,原侦听socket继续侦听。

 

简单聊天室

服务端

public partial class FChatServer : Form
    {
        public FChatServer()
        {
            InitializeComponent();
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        Thread threadWatch=null;
        Socket socketWatch = null;

        private void button1_Click(object sender, EventArgs e)
        {
            //创建 服务端负责监听的套接字,参数(使用IP4地址协议,使用流式连接,使用TCP协议传输数据);
            socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //获取 文本框IP地址对象
            IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
            //创建 包含ip和port的网络节点对象
            IPEndPoint endpoint = new IPEndPoint(address,int.Parse(txtPort.Text.Trim()));
            //将负责监听的套接字绑定到唯一的IP和端口上
            socketWatch.Bind(endpoint);
            //设置监听队列长度
            socketWatch.Listen(10);
            //创建负责监听的线程,传入监听方法
            threadWatch = new Thread(WatchConnecting);
            threadWatch.IsBackground = true;
            threadWatch.Start();

            ShowMsg("服务器启动监听成功!");
        }

        //保存了服务器端所有负责和客服端通信的套接字
        Dictionary dict = new Dictionary();
        //保存了服务器端所有负责调用通信套接字Recive方法的线程
        Dictionary dictThread = new Dictionary();
         
        /// 
        /// 监听 客服端请求的方法
        /// 
        /// 
        void WatchConnecting()
        {
            //持续不断的监听新客服端的连接请求
            while (true)
            {
                //开始监听客服端连接请求,accept方法,会阻断当前的线程
                Socket sokConnection = socketWatch.Accept();//一旦监听到客服端的请求 ,就返回一个负责和该客服端通信的套接字sokConnection 
                //向列表控件中添加一个客服端的IP端口字符串,作为 客服端的唯一标识
                lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
                //将与客服端通信的套接字对象sokConnection添加到键值对集合中,并以客服端IP端口作为键
                dict.Add(sokConnection.RemoteEndPoint.ToString(),sokConnection);

                //创建通信线程
                Thread thr = new Thread(RecMsg);
                thr.IsBackground = true;
                thr.Start(sokConnection);
                dictThread.Add(sokConnection.RemoteEndPoint.ToString(),thr);

                ShowMsg("客服端连接成功!");
            }
        }

        /// 
        /// 服务端负责监听客服端发送来的数据的方法
        /// 
        void RecMsg(object socketClientPara)
        {
            Socket socketClient = socketClientPara as Socket;
            while (true)
            {
                //定义一个接收用的缓存区(2M字节数组)
                byte[] arrMsgRec = new byte[1024 * 1024 * 2];
                //将接收到的数据存入arrMsgRec数组,返回真正就收到的数据长度
                int length = -1;
                try
                {
                    length = socketClient.Receive(arrMsgRec);
                }
                catch (SocketException ex)
                {
                    ShowMsg("异常:" + ex.Message);
                    //从通信套接字集合中删除被中断连接的通信套接字对象
                    dict.Remove(socketClient.RemoteEndPoint.ToString());
                    //从通信线程集合中删除被终端连接的通信线程对象
                    dictThread.Remove(socketClient.RemoteEndPoint.ToString());
                    //从列表中移除被中断的ip:port
                    lbOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());
                    break;
                }
                catch (Exception ex)
                {
                    ShowMsg("异常:" + ex.Message);
                    break;
                }
                if (arrMsgRec[0] == 0)//判断发送过来的数据的第一个元素是0,则代表发送来的是文字数据
                {
                    //此时是将数组的所有元素都转成字符串,而真正接收到的只有服务器发来的几个字符
                    string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length-1);
                    ShowMsg(strMsgRec);
                }
                else if (arrMsgRec[0] == 1)//如果是1,代表发送来的是文件数据(图片/视频/文件...)
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    if (sfd.ShowDialog() == DialogResult.OK)
                    {
                        string fileSavePath = sfd.FileName;//获得要保存的文件路径
                        using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
                        {
                            fs.Write(arrMsgRec,1,length-1);
                            ShowMsg("文件保存成功");
                        }
                    }
                }
            }
        }
                
        void ShowMsg(string msg)
        {
            txtMessage.AppendText(msg+"\r\n");
        }

        //发送消息到客服端
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(lbOnline.Text))
            {
                MessageBox.Show("请选择要发送的好友");
                return;
            }
            string strMsg = txtMsgSend.Text.Trim();
            //将要发送的字符串转成utf-8对应的字节数组
            byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
            //获得列表中选中的KEY
            string strClientKey = lbOnline.Text;
            //通过KEY,找到字典集合中对应的与某个 客服端通信的套接字的send方法,发送数据给对方
            try
            {
                dict[strClientKey].Send(arrMsg);
                ShowMsg("发送了数据:" + strMsg);
            }
            catch (SocketException ex)
            {
                ShowMsg("发送时异常:" + ex.Message);
                return;
            }
            catch (Exception ex)
            {

                ShowMsg("发送时异常:" + ex.Message);
                return;
            }
        }

        private void btnSendAll_Click(object sender, EventArgs e)
        {
            string strMsg = txtMsgSend.Text.Trim();
            byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
            try
            {
                foreach (Socket s in dict.Values)
                {
                    s.Send(arrMsg);
                }
                ShowMsg("群发完毕!");
            }
            catch (SocketException ex)
            {
                ShowMsg("发送时异常:" + ex.Message);
                return;
            }
            catch (Exception ex)
            {

                ShowMsg("发送时异常:" + ex.Message);
                return;
            }
        }
    }


客服端

 public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        Thread threadClient = null;//客服端负责接收服务端发来数据消息的线程
        Socket socketClient=null;

        private void btnConnect_Click(object sender, EventArgs e)
        {
            IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
            IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
            socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socketClient.Connect(endpoint);
            threadClient = new Thread(RecMsg);
            threadClient.IsBackground = true;
            threadClient.Start();
        }

        void RecMsg()
        {
            while (true)
            {
                //定义一个接收用的缓存区(2M字节数组)
                byte[] arrMsgRec = new byte[1024 * 1024 * 2];
                //将接收到的数据存入arrMsgRec数组,返回真正就收到的数据长度
                int length=socketClient.Receive(arrMsgRec);
                //此时是将数组的所有元素都转成字符串,而真正接收到的只有服务器发来的几个字符
                string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,0,length);
                ShowMsg(strMsgRec);
            }
        }

        void ShowMsg(string msg)
        {
            txtMessage.AppendText(msg + "\r\n");
        }

        //向服务器发送文本消息
        private void btnSend_Click(object sender, EventArgs e)
        {
            string strMsg = txtSendMsg.Text.Trim();
            //将字符串转成方便网络传送的二进制数据
            byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
            byte[] arrMsgSend=new byte[arrMsg.Length+1];
            arrMsgSend[0] = 0;//设置标识位
            Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length);            
            try
            {
                socketClient.Send(arrMsgSend);
                ShowMsg("我说:"+strMsg);
              
            }
            catch (SocketException ex)
            {
                ShowMsg("发送时异常:" + ex.Message);
                return;
            }
            catch (Exception ex)
            {

                ShowMsg("发送时异常:" + ex.Message);
                return;
            }
        }

        //选择要发送的文件
        private void btnChooseFile_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                txtFilePath.Text = ofd.FileName;
            }
        }

        //发送文件
        private void btnSendFile_Click(object sender, EventArgs e)
        {
            //用文件流打开用户选择的文件
            using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open))
            {
                byte[] arrFile = new byte[1024 * 1024 * 2];//定义一个2M的数组(缓存区)
                //将文件数据读到数组arrFile中,并获得读取的真实数据长度Length
                int length=fs.Read(arrFile,0,arrFile.Length);
                byte[] arrFileSend=new byte[length+1];
                arrFileSend[0] = 1;//代表发送的是文件数据
                //将arrFile数组里的元素从第0个开始拷贝,拷贝到arrFileSend数组里,从第1个开始存放,一共拷贝length个数据
                Buffer.BlockCopy(arrFile,0,arrFileSend,1,length);
                try
                {
                    //发送包含了标识位的新数据数组到服务端
                    socketClient.Send(arrFileSend);
                }
                catch (SocketException ex)
                {
                    ShowMsg("发送时异常:" + ex.Message);
                    return;
                }
                catch (Exception ex)
                {

                    ShowMsg("发送时异常:" + ex.Message);
                    return;
                }
            }
        }
    }

 

上面的代码没实现客服端之间的消息传送

---------------------- Windows Phone 7手机开发、Net培训、期待与您交流! ----------------------

详细请查看: http://net.itheima.com

你可能感兴趣的:(聊天,socket,exception,thread,byte,string)