C# Socket通信简单实例代码

Socket是网络编程必须要了解的一块,C#对Socket的封装还是蛮好的,用起来非常方便。下面是一个非常简单的实例代码。

客户端 主要有下面几个关键点:

1. 用Socket对象连接server, 连接成功后用Thread启动receive的轮询,如果接收到新的数据到达就解码然后更新。

2. 如果有发送,直接调用Send()函数发送编码好的字符。

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


        Socket sokClient = null;//负责与服务端通信的套接字
        Thread threadClient = null;//负责 监听 服务端发送来的消息的线程
        bool isRec = true;//是否循环接收服务端数据

        private byte[] GetKeepAliveData()
        {
            uint dummy = 0;
            byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
            BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
            BitConverter.GetBytes((uint)3000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//3s keep-alive间隔
            BitConverter.GetBytes((uint)500).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);//0.5s 尝试间隔
            return inOptionValues;
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            //实例化 套接字
            sokClient = 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()));
            //连接 服务端监听套接字
            sokClient.Connect(endpoint);
            //创建负责接收 服务端发送来数据的 线程
            threadClient = new Thread(ReceiveMsg);
            threadClient.IsBackground = true;
            //如果在win7下要通过 某个线程 来调用 文件选择框的代码,就需要设置如下
            threadClient.SetApartmentState(ApartmentState.STA);
            threadClient.Start();
        }

        /// <summary>
        /// 接收服务端发送来的消息数据
        /// </summary>
        void ReceiveMsg()
        {
            while (isRec)
            {
                byte[] msgArr = new byte[1024 * 1024 * 1];//接收到的消息的缓冲区
                int length = 0;
                //接收服务端发送来的消息数据
                length = sokClient.Receive(msgArr);//Receive会阻断线程
                    string strMsg = System.Text.Encoding.UTF8.GetString(msgArr, 1, length - 1);
                    txtShow.AppendText(strMsg + "\r\n");
            }
        }


        //发送消息
        private void btnSend_Click(object sender, EventArgs e)
        {
            byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(txtInput.Text.Trim());
            sokClient.Send(arrMsg);
        }
    }

服务器端的代码和注意点

1. 需要一个socket对象用来做Listening (监听),使用Thread启动轮询

2. 每次有客户端连接请求之后,都要启动一个Socket实例去handle和这个客户端的通信,

3. 这个Socket对象相当于一个完整的客户端Socket,代码也类似于客户端的socket代码

 public partial class Form1 : Form
    {
        Socket sokWatch = null;//负责监听 客户端段 连接请求的  套接字
        Thread threadWatch = null;//负责 调用套接字, 执行 监听请求的线程

        public Form()
        {
            InitializeComponent();
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        private void start_Click(object sender, EventArgs e)
        {
            //实例化 套接字 (ip4寻址协议,流式传输,TCP协议)
            sokWatch = 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和端口
            sokWatch.Bind(endpoint);
            //设置 监听队列 长度为10(同时能够处理 10个连接请求)
            sokWatch.Listen(10);
            threadWatch = new Thread(StartWatch);
            threadWatch.IsBackground = true;
            threadWatch.Start();
            txtShow.AppendText("启动服务器成功......\r\n");
        }

        Dictionary<string, ConnectionClient> dictConn = new Dictionary<string, ConnectionClient>();

        bool isWatch = true;
        //监听函数
        void StartWatch()
        {
            while (isWatch)
            {
                //threadWatch.SetApartmentState(ApartmentState.STA);
                //监听 客户端 连接请求,但是,Accept会阻断当前线程
                Socket sokMsg = sokWatch.Accept();//监听到请求,立即创建负责与该客户端套接字通信的套接字
                ConnectionClient connection = new ConnectionClient(sokMsg, ShowMsg, RemoveClientConnection);
                //将负责与当前连接请求客户端 通信的套接字所在的连接通信类 对象 装入集合
                dictConn.Add(sokMsg.RemoteEndPoint.ToString(), connection);
                //将 通信套接字 加入 集合,并以通信套接字的远程IpPort作为键
                //dictSocket.Add(sokMsg.RemoteEndPoint.ToString(), sokMsg);
                //将 通信套接字的 客户端IP端口保存在下拉框里
                cboClient.Items.Add(sokMsg.RemoteEndPoint.ToString());
                ShowMsg("接收连接成功......");
                //启动一个新线程,负责监听该客户端发来的数据
                //Thread threadConnection = new Thread(ReciveMsg);
                //threadConnection.IsBackground = true;
                //threadConnection.Start(sokMsg);
            }
        }

        bool isRec = true;//与客户端通信的套接字 是否 监听消息

        //发送消息 到指定的客户端
        private void btnSend_Click(object sender, EventArgs e)
        {
            //byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(txtInput.Text.Trim());
            //从下拉框中 获得 要哪个客户端发送数据
            string connectionSokKey = cboClient.Text;
            if (!string.IsNullOrEmpty(connectionSokKey))
            {
                //从字典集合中根据键获得 负责与该客户端通信的套接字,并调用send方法发送数据过去
                dictConn[connectionSokKey].Send(txtInput.Text.Trim());
                //sokMsg.Send(arrMsg);
            }
            else
            {
                MessageBox.Show("请选择要发送的客户端~~");
            }
        }

       
        //向文本框显示消息 -void ShowMsg(string msgStr)
        public void ShowMsg(string msgStr)
        {
            txtShow.AppendText(msgStr + "\r\n");
        }

        private void btnSendMsgAll_Click(object sender, EventArgs e)
        {
            foreach (ConnectionClient conn in dictConn.Values)
            {
                conn.Send(txtInput.Text.Trim());
            }
        }
    }

负责和每个客户端通信的Socket对象 

 public delegate void DGShowMsg(string strMsg);
    public class ConnectionClient
    {
        Socket sokMsg;
        DGShowMsg dgShowMsg;//负责 向主窗体文本框显示消息的方法委托
        DGShowMsg dgRemoveConnection;// 负责 从主窗体 中移除 当前连接
        Thread threadMsg;

        public ConnectionClient(Socket sokMsg, DGShowMsg dgShowMsg, DGShowMsg dgRemoveConnection)
        {
            this.sokMsg = sokMsg;
            this.dgShowMsg = dgShowMsg;
            this.dgRemoveConnection = dgRemoveConnection;

            this.threadMsg = new Thread(RecMsg);
            this.threadMsg.IsBackground = true;
            this.threadMsg.Start();
        }

        bool isRec = true;
       // 02负责监听客户端发送来的消息
        void RecMsg()
        {
            while (isRec)
            {
                try
                {
                    byte[] arrMsg = new byte[1024 * 1024 * 2];
                    //接收 对应 客户端发来的消息
                    int length = sokMsg.Receive(arrMsg);
                    //将接收到的消息数组里真实消息转成字符串
                    string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg, 0, length);
                    //通过委托 显示消息到 窗体的文本框
                    dgShowMsg(strMsg);
                }
                catch (Exception ex)
                {
                    isRec = false;
                    //从主窗体中 移除 下拉框中对应的客户端选择项,同时 移除 集合中对应的 ConnectionClient对象
                    dgRemoveConnection(sokMsg.RemoteEndPoint.ToString());
                }
            }
        }

        // 03向客户端发送消息
        public void Send(string strMsg)
        {
            byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
            byte[] arrMsgFinal = new byte[arrMsg.Length + 1];

            arrMsgFinal[0] = 0;//设置 数据标识位等于0,代表 发送的是 文字
            arrMsg.CopyTo(arrMsgFinal, 1);

            sokMsg.Send(arrMsgFinal);
        }

        // 04向客户端发送文件数据 +void SendFile(string strPath)

        public void SendFile(string strPath)
        {
            //通过文件流 读取文件内容
            using (FileStream fs = new FileStream(strPath, FileMode.OpenOrCreate))
            {
                byte[] arrFile = new byte[1024 * 1024 * 2];
                //读取文件内容到字节数组,并 获得 实际文件大小
                int length = fs.Read(arrFile, 0, arrFile.Length);
                //定义一个 新数组,长度为文件实际长度 +1
                byte[] arrFileFina = new byte[length + 1];
                arrFileFina[0] = 1;//设置 数据标识位等于1,代表 发送的是文件
                //将 文件数据数组 复制到 新数组中,下标从1开始
                //arrFile.CopyTo(arrFileFina, 1);
                Buffer.BlockCopy(arrFile, 0, arrFileFina, 1, length);
                //发送文件数据
                sokMsg.Send(arrFileFina);//, 0, length + 1, SocketFlags.None);
            }
        }

        public void CloseConnection()
        {
            isRec = false;
        }
    }

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值