黑马程序员-简单聊天室

来源:互联网 发布:办公室网络被限制 编辑:程序博客网 时间:2024/05/19 19:14

---------------------- 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<string, Socket> dict = new Dictionary<string, Socket>();        //保存了服务器端所有负责调用通信套接字Recive方法的线程        Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();                 /// <summary>        /// 监听 客服端请求的方法        /// </summary>        /// <param name="msg"></param>        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("客服端连接成功!");            }        }        /// <summary>        /// 服务端负责监听客服端发送来的数据的方法        /// </summary>        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