
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.IO;
namespace socket
{
    public partial class 服务端 : Form
    {
        public 服务端()
        {
            InitializeComponent();
            TextBox.CheckForIllegalCrossThreadCalls = false;//关闭对文本框的跨线程操作的检查
        }
        Socket socketWatch;
        //保存了所有和客户端通信的套接字
        Dictionary<string, Socket> dict = new Dictionary<string, Socket>();
        private void button1_Click(object sender, EventArgs e)
        {
            //创建服务端负责监听的套接字,参数(使用IPV4寻址协议,使用流式连接,使用Tcp协议传输数据);
            socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
            //获得文本框的Ip地址对象
            IPAddress address = IPAddress.Parse(textBox1.Text);
            
            //创建包含ip和端口的网络节点对象
            IPEndPoint endpoint = new IPEndPoint(address,int.Parse(textBox2.Text));
            
            //将负责监听的套接字绑定到唯一的ip和端口上
            try
            {
                socketWatch.Bind(endpoint);
                
            }
            catch (Exception ex)
            {
                textBox3.AppendText("异常:" + ex.Message + "\r\n");
                return;
            }
            
            //设置监听队列的长度
            socketWatch.Listen(10);
            
            //开始监听客户端连接请求,注意:accept会阻断当前的线程,打开另外一个线程
            //socketWatch.Accept();
           
            //创建负责监听的线程
            Thread threadWatch = new Thread(WatchConnecting);
            threadWatch.IsBackground = true;
            threadWatch.Start();
            textBox3.AppendText("服务器监听成功!\r\n");//\r\n换行
        }
        void WatchConnecting()
        {
            while (true)//持续不断的监听新的客户端的连接请求
            {
                //一旦监听到客户端的请求,就返回一个负责和该客户端通信的套接字
                Socket connection; 
                try
                {
                    connection = socketWatch.Accept();
                }
                catch (Exception ex)
                {
                    textBox3.AppendText("异常:" + ex.Message + "\r\n");
                    break;
                }
                //将与客户端通信的套接字对象connection添加到键值对集合中,并以客户端ip端口作为键
                dict.Add(connection.RemoteEndPoint.ToString(), connection);
                //向列表控件中添加一个客户端的ip端口字符串,作为客户端的唯一标识
                listBox1.Items.Add(connection.RemoteEndPoint.ToString());
                textBox3.AppendText("客户端连接成功!\r\n");
                //connection.RemoteEndPoint中保存的是当前连接客户端的ip和端口
                
                //为连接创建一个接收的线程
                Thread rec = new Thread(receive);
                rec.IsBackground = true;
                rec.Start(connection);//启动线程,当前连接传入套接字
            }
        }
        void receive(object conn)
        {
            Socket message=conn as Socket;
            while (true)
            {
                byte[] msg = new byte[1024 * 1024 * 2];
                //接收到的信息的长度
                int length=-1;
                try
                {
                    length = message.Receive(msg);
                }
                catch (SocketException ex)
                {
                    textBox3.AppendText("异常:" + ex.Message);
                    //从通信套接字集合中移除被中断的通信套接字对象
                    dict.Remove(message.RemoteEndPoint.ToString());
                    //从列表中清除中断的连接
                    listBox1.Items.Remove(message.RemoteEndPoint.ToString());
                    break;
                }
                catch (Exception ex)
                {
                    textBox3.AppendText("异常:" + ex.Message);
                    break;
                }
                if (msg[0] == 0)
                {
                    textBox3.AppendText("接收数据:" + System.Text.Encoding.UTF8.GetString(msg, 1, length-1) + "\r\n");
                }
                //在客户端显示
                else if (msg[0] == 1)
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    if (sfd.ShowDialog() == DialogResult.OK)
                    {
                        string path = sfd.FileName;//获取路径
                        //创建文件流,然后让文件流来根据路径创建一个文件
                        using (FileStream fs = new FileStream(path, FileMode.Create))
                        {
                            fs.Write(msg, 1, length - 1);
                            textBox3.AppendText("文件保存成功:" + path + "\r\n");
                        }
                    }
                }
            }
        }
        //发送信息
        private void button2_Click(object sender, EventArgs e)
        {
            if (listBox1.Text=="")
            {
                MessageBox.Show("请选择好友");
            }
            else
            {
                //将要发送的字符串转成utf8对应的字节数组
                byte[] message = System.Text.Encoding.UTF8.GetBytes(textBox4.Text);
                //获得列表中选中key
                string key = listBox1.Text;
                //通过key找到字典集合中对应的与某个客户端通信套接字的send方法,发送数据给对方
                dict[key].Send(message);
                //connection.Send(message);//connection不能用socketWatch.Accept()代替
                textBox3.AppendText("发送数据:" + textBox4.Text + "\r\n");
            }
        }
        //群发信息
        private void button3_Click(object sender, EventArgs e)
        {
            byte[] message = System.Text.Encoding.UTF8.GetBytes(textBox4.Text);
            foreach (Socket s in dict.Values)
            {
                s.Send(message);
            }
        }
    }
}