当前位置: 移动技术网 > IT编程>开发语言>.net > C# Socket异步实现消息发送--附带源码

C# Socket异步实现消息发送--附带源码

2019年03月07日  | 移动技术网IT编程  | 我要评论
前言 看了一百遍,不如动手写一遍。 Socket这块使用不是特别熟悉,之前实现是公司有对应源码改改能用。 但是不理解实现的过程和步骤,然后最近有时间自己写个demo实现看看,熟悉熟悉Socket。 网上也有好的文章,结合别人的理接和自己实践总算写完了。。。 参考: https://www.cnblo ...

前言

看了一百遍,不如动手写一遍。

socket这块使用不是特别熟悉,之前实现是公司有对应源码改改能用。

但是不理解实现的过程和步骤,然后最近有时间自己写个demo实现看看,熟悉熟悉socket。

网上也有好的文章,结合别人的理接和自己实践总算写完了。。。

参考:实现

参考:

         

理解握手过程,关闭时的握手过程(关闭的过程弄了半天,总算弄懂了意思)。

实现过程

总体包含:开启,关闭,断线重连(客户端),内容发送。

说明:服务端和客户端代码基本一直,客户端需要实时监听服务端状态断开时需要重连。

页面效果图:

服务端实现(实现不包含ui部分 最后面放所有代码和下载地址)

给出一个指定的地址ip+port,套接字类型初始化socket

使用bind方法进行关联,listen(1) 注释: 挂起连接队列的最大长度。我的通俗理接:你有一个女朋友这是你不能找别的,但是如果分手了就可以找别的,

beginaccept包含一个异步的回调,获取请求的socket

var s_socket = new socket(addressfamily.internetwork,sockettype.stream, protocoltype.tcp);
s_socket.bind(new ipendpoint(ipaddress.parse(ip), port));
s_socket.listen(10);//同时连接的最大连接数
s_socket.beginaccept(new asynccallback(accept), s_socket);//获取连接
view code

clientstate,自己声明的一个类用于接收数据(对应readcallback)和内部处理

accept异步请求回调,当有socket请求时会进去该回调,

endaccept,我的理接为给当前socket创建一个新的链接释放掉 listen

beginreceive,包含一个消息回调(readcallback),只需给出指定长度数组接收socket上传的消息数据

beginaccept,再次执行等待方法等待后续socket连接

        /// <summary>
        /// 异步连接回调 获取请求socket 添加信息到控件
        /// </summary>
        /// <param name="ar"></param>
        private void accept(iasyncresult ar)
        {
            try
            {
                //获取连接socket 创建新的连接
                socket myserver = ar.asyncstate as socket;
                socket service = myserver.endaccept(ar);

                clientstate obj = new clientstate();
                obj.clientsocket = service;
                //接收连接socket数据
                service.beginreceive(obj.buffer, 0, clientstate.bufsize, socketflags.none, new asynccallback(readcallback), obj);
                myserver.beginaccept(new asynccallback(accept), myserver);//等待下一个连接
            }
            catch (exception ex)
            {
                console.writeline("服务端关闭"+ex.message+" "+ex.stacktrace);
            }
        }
view code

endreceive,通俗理接买东西的时候老板已经打包好了,付钱走人。

beginreceive,当数据处理完成之后,和socket连接一样需再次执行获取下次数据

         /// <summary>
        /// 数据接收
        /// </summary>
        /// <param name="ar">请求的socket</param>
        private void readcallback(iasyncresult ar)
        {
            //获取并保存
            clientstate obj = ar.asyncstate as clientstate;
            socket c_socket = obj.clientsocket;
            int bytes = c_socket.endreceive(ar);                    
            //接收完成 重新给出buffer接收
            obj.buffer = new byte[clientstate.bufsize];
            c_socket.beginreceive(obj.buffer, 0, clientstate.bufsize, 0, new asynccallback(readcallback), obj);
         }
view code

send,消息发送比较简单,将发送的数组转成数组的形式进行发送

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="s_socket">指定客户端socket</param>
        /// <param name="message">发送消息</param>
        /// <param name="shake">发送消息</param>
        private void send(socket c_socket, byte[] by)
        {
               //发送
                c_socket.beginsend(by, 0, by.length, socketflags.none, asyncresult =>
                {
                    try
                    {
                        //完成消息发送
                        int len = c_socket.endsend(asyncresult);
                    }
                    catch (exception ex)
                    {
                        if (c_socket != null)
                        {
                            c_socket.close();
                            c_socket = null;
                        }
                        console.writeline("error ex=" + ex.message + " " + ex.stacktrace);
                    }
                }, null);
        }
view code

 

客户端实现(实现不包含ui部分 最后面放所有代码和下载地址)

相对于服务端差别不大,只是需要在链接之后增加一个监听机制,进行断线重连,我写的这个比较粗糙。

beginconnect,使用指定的地址ip+port进新一个异步的链接

connect,链接回调

connectionresult,初始值为-2 在消息接收(可检测服务端断开),链接(链接失败)重新赋值然后判断是否为错误码然后重连

        /// <summary>
        /// 连接socket
        /// </summary>
        public void start()
        {
            try
            {
                var endpoint = new ipendpoint(ipaddress.parse(ip), port);
                c_socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
                c_socket.beginconnect(endpoint, new asynccallback(connect), c_socket);//链接服务端
                th_socket = new thread(monitorsocker);//监听线程
                th_socket.isbackground = true;
                th_socket.start();           
           }
           catch (socketexception ex)
           {
                console.writeline("error ex=" + ex.message + " " + ex.stacktrace);
            }
        }

        //监听socket
        void monitorsocker()
        {
            while (true)
            {
                if (connectionresult != 0 && connectionresult != -2)//通过错误码判断
                {
                    start();
                }
                thread.sleep(1000);
            }
        }
view code

 

connec链接部分

/// <summary>
        /// 连接服务端
        /// </summary>
        /// <param name="ar"></param>
        private void connect(iasyncresult ar)
        {
            try
            {
                servicestate obj = new servicestate();
                socket client = ar.asyncstate as socket;
                obj.servicesocket = client;
                //获取服务端信息
                client.endconnect(ar);
                //接收连接socket数据 
                client.beginreceive(obj.buffer, 0, servicestate.bufsize, socketflags.none, new asynccallback(readcallback), obj);
catch (socketexception ex)
            {
                connectionresult = ex.errorcode;
                console.writeline(ex.message + " " + ex.stacktrace);
            }
        }
view code

 

整体数据发送和实现部分

声明一个指定的类,给出指定的标识,方便数据处理,

在发送文字消息,图片消息,震动时需要对应的判断其实最简便的方法是直接发送一个xml报文过去直接解析,

也可以采用在头部信息里面直接给出传送类型。

数组中给出了一个0-15的信息头

0-3 标识码,确认身份

4-7 总长度,总体长度可用于接收时判断所需长度

8-11 内容长度,判断内容是否接收完成

12-15 补0,1111(震动) 2222(图片数据,图片这块为了接收图片名称所以采用xml报文形式发送)

16开始为内容

/// <summary>
    /// 0-3 标识码 4-7 总长度 8-11 内容长度 12-15补0 16开始为内容
    /// </summary>
    public class sendsocket
    {
        /// <summary>
        /// 头 标识8888
        /// </summary>
        byte[] header = new byte[4] { 0x08, 0x08, 0x08, 0x08 };

        /// <summary>
        /// 文本消息
        /// </summary>
        public string message;

        /// <summary>
        /// 是否发送震动
        /// </summary>
        public bool sendshake = false;

        /// <summary>
        /// 是否发送图片
        /// </summary>
        public bool sendimg = false;

        /// <summary>
        /// 图片名称
        /// </summary>
        public string imgname;

        /// <summary>
        /// 图片数据
        /// </summary>
        public string imgbase64;
        /// <summary>
        /// 组成特定格式的byte数据
        /// 12-15 为指定发送内容 1111(震动) 2222(图片数据)
        /// </summary>
        /// <param name="mes">文本消息</param>
        /// <param name="shake">震动</param>
        /// <param name="img">图片</param>
        /// <returns>特定格式的byte</returns>
        public byte[] toarray()
        {
            if (sendimg)//是否发送图片
            {
                //组成xml接收 可以接收相关图片数据
                stringbuilder xmlresult = new stringbuilder("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
                xmlresult.append("<imgmessage>");
                xmlresult.appendformat("<imgname>{0}</imgname>", imgname);
                xmlresult.appendformat("<imgbase64>{0}</imgbase64>", imgbase64);
                xmlresult.append("</imgmessage>");
                message = xmlresult.tostring();
            }

            byte[] bytedata = encoding.utf8.getbytes(message);//内容
            int count = 16 + bytedata.length;//总长度
            byte[] sendby = new byte[count];
            array.copy(header, 0, sendby, 0, header.length);//添加头

            byte[] countby = bitconverter.getbytes(count);
            array.copy(countby, 0, sendby, 4, countby.length);//总长度

            byte[] contentby = bitconverter.getbytes(bytedata.length);
            array.copy(contentby, 0, sendby, 8, contentby.length);//内容长度

            if (sendshake)//发动震动
            {
                var shakeby = new byte[4] { 1, 1, 1, 1 };
                array.copy(shakeby, 0, sendby, 12, shakeby.length);//震动
            }

            if (sendimg)//发送图片
            {
                var imgby = new byte[4] { 2, 2, 2, 2 };
                array.copy(imgby, 0, sendby, 12, imgby.length);//图片
            }

            array.copy(bytedata, 0, sendby, 16, bytedata.length);//内容
            return sendby;
        }
    }
view code

 

代码:

服务端:

窗体(ui)代码:

namespace socketservice
{
    partial class form1
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private system.componentmodel.icontainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.dispose();
            }
            base.dispose(disposing);
        }

        #region windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void initializecomponent()
        {
            this.label1 = new system.windows.forms.label();
            this.txt_ip = new system.windows.forms.textbox();
            this.txt_port = new system.windows.forms.textbox();
            this.label2 = new system.windows.forms.label();
            this.btn_startsocket = new system.windows.forms.button();
            this.txt_monitor = new system.windows.forms.textbox();
            this.label3 = new system.windows.forms.label();
            this.groupbox1 = new system.windows.forms.groupbox();
            this.btn_sendimg = new system.windows.forms.button();
            this.btn_sendshake = new system.windows.forms.button();
            this.btn_sendmes = new system.windows.forms.button();
            this.txt_mes = new system.windows.forms.textbox();
            this.label4 = new system.windows.forms.label();
            this.datagridview1 = new system.windows.forms.datagridview();
            this.column1 = new system.windows.forms.datagridviewtextboxcolumn();
            this.column2 = new system.windows.forms.datagridviewtextboxcolumn();
            this.groupbox2 = new system.windows.forms.groupbox();
            this.lab_imgname = new system.windows.forms.label();
            this.picturebox1 = new system.windows.forms.picturebox();
            this.listbox_mes = new system.windows.forms.listbox();
            this.listbox_attribute = new system.windows.forms.listbox();
            this.openfiledialog1 = new system.windows.forms.openfiledialog();
            this.btn_stop = new system.windows.forms.button();
            this.label5 = new system.windows.forms.label();
            this.label6 = new system.windows.forms.label();
            this.label7 = new system.windows.forms.label();
            this.groupbox1.suspendlayout();
            ((system.componentmodel.isupportinitialize)(this.datagridview1)).begininit();
            this.groupbox2.suspendlayout();
            ((system.componentmodel.isupportinitialize)(this.picturebox1)).begininit();
            this.suspendlayout();
            // 
            // label1
            // 
            this.label1.autosize = true;
            this.label1.location = new system.drawing.point(13, 13);
            this.label1.name = "label1";
            this.label1.size = new system.drawing.size(17, 12);
            this.label1.tabindex = 0;
            this.label1.text = "ip";
            // 
            // txt_ip
            // 
            this.txt_ip.location = new system.drawing.point(36, 10);
            this.txt_ip.name = "txt_ip";
            this.txt_ip.size = new system.drawing.size(100, 21);
            this.txt_ip.tabindex = 1;
            this.txt_ip.text = "127.0.0.1";
            // 
            // txt_port
            // 
            this.txt_port.location = new system.drawing.point(183, 10);
            this.txt_port.name = "txt_port";
            this.txt_port.size = new system.drawing.size(100, 21);
            this.txt_port.tabindex = 3;
            this.txt_port.text = "9999";
            // 
            // label2
            // 
            this.label2.autosize = true;
            this.label2.location = new system.drawing.point(148, 15);
            this.label2.name = "label2";
            this.label2.size = new system.drawing.size(29, 12);
            this.label2.tabindex = 2;
            this.label2.text = "端口";
            // 
            // btn_startsocket
            // 
            this.btn_startsocket.location = new system.drawing.point(307, 8);
            this.btn_startsocket.name = "btn_startsocket";
            this.btn_startsocket.size = new system.drawing.size(75, 23);
            this.btn_startsocket.tabindex = 4;
            this.btn_startsocket.text = "开始监听";
            this.btn_startsocket.usevisualstylebackcolor = true;
            this.btn_startsocket.click += new system.eventhandler(this.btn_startsocket_click);
            // 
            // txt_monitor
            // 
            this.txt_monitor.location = new system.drawing.point(460, 8);
            this.txt_monitor.name = "txt_monitor";
            this.txt_monitor.readonly = true;
            this.txt_monitor.size = new system.drawing.size(155, 21);
            this.txt_monitor.tabindex = 6;
            // 
            // label3
            // 
            this.label3.autosize = true;
            this.label3.location = new system.drawing.point(401, 11);
            this.label3.name = "label3";
            this.label3.size = new system.drawing.size(53, 12);
            this.label3.tabindex = 5;
            this.label3.text = "正在监听";
            // 
            // groupbox1
            // 
            this.groupbox1.controls.add(this.btn_sendimg);
            this.groupbox1.controls.add(this.btn_sendshake);
            this.groupbox1.controls.add(this.btn_sendmes);
            this.groupbox1.controls.add(this.txt_mes);
            this.groupbox1.controls.add(this.label4);
            this.groupbox1.controls.add(this.datagridview1);
            this.groupbox1.location = new system.drawing.point(13, 68);
            this.groupbox1.name = "groupbox1";
            this.groupbox1.size = new system.drawing.size(681, 275);
            this.groupbox1.tabindex = 8;
            this.groupbox1.tabstop = false;
            this.groupbox1.text = "客户端连接列表";
            // 
            // btn_sendimg
            // 
            this.btn_sendimg.location = new system.drawing.point(466, 246);
            this.btn_sendimg.name = "btn_sendimg";
            this.btn_sendimg.size = new system.drawing.size(75, 23);
            this.btn_sendimg.tabindex = 11;
            this.btn_sendimg.text = "发送图片";
            this.btn_sendimg.usevisualstylebackcolor = true;
            this.btn_sendimg.click += new system.eventhandler(this.btn_sendimg_click);
            // 
            // btn_sendshake
            // 
            this.btn_sendshake.location = new system.drawing.point(380, 246);
            this.btn_sendshake.name = "btn_sendshake";
            this.btn_sendshake.size = new system.drawing.size(75, 23);
            this.btn_sendshake.tabindex = 10;
            this.btn_sendshake.text = "发送震动";
            this.btn_sendshake.usevisualstylebackcolor = true;
            this.btn_sendshake.click += new system.eventhandler(this.btn_sendshake_click);
            // 
            // btn_sendmes
            // 
            this.btn_sendmes.location = new system.drawing.point(294, 246);
            this.btn_sendmes.name = "btn_sendmes";
            this.btn_sendmes.size = new system.drawing.size(75, 23);
            this.btn_sendmes.tabindex = 9;
            this.btn_sendmes.text = "发送";
            this.btn_sendmes.usevisualstylebackcolor = true;
            this.btn_sendmes.click += new system.eventhandler(this.btn_sendmes_click);
            // 
            // txt_mes
            // 
            this.txt_mes.location = new system.drawing.point(294, 50);
            this.txt_mes.multiline = true;
            this.txt_mes.name = "txt_mes";
            this.txt_mes.size = new system.drawing.size(368, 190);
            this.txt_mes.tabindex = 2;
            // 
            // label4
            // 
            this.label4.autosize = true;
            this.label4.location = new system.drawing.point(292, 35);
            this.label4.name = "label4";
            this.label4.size = new system.drawing.size(53, 12);
            this.label4.tabindex = 1;
            this.label4.text = "消息输入";
            // 
            // datagridview1
            // 
            this.datagridview1.allowusertoordercolumns = true;
            this.datagridview1.columnheadersheightsizemode = system.windows.forms.datagridviewcolumnheadersheightsizemode.autosize;
            this.datagridview1.columns.addrange(new system.windows.forms.datagridviewcolumn[] {
            this.column1,
            this.column2});
            this.datagridview1.location = new system.drawing.point(7, 35);
            this.datagridview1.name = "datagridview1";
            this.datagridview1.rowtemplate.height = 23;
            this.datagridview1.size = new system.drawing.size(249, 234);
            this.datagridview1.tabindex = 0;
            // 
            // column1
            // 
            this.column1.headertext = "ip";
            this.column1.name = "column1";
            // 
            // column2
            // 
            this.column2.headertext = "port";
            this.column2.name = "column2";
            // 
            // groupbox2
            // 
            this.groupbox2.controls.add(this.label7);
            this.groupbox2.controls.add(this.label6);
            this.groupbox2.controls.add(this.label5);
            this.groupbox2.controls.add(this.lab_imgname);
            this.groupbox2.controls.add(this.picturebox1);
            this.groupbox2.controls.add(this.listbox_mes);
            this.groupbox2.controls.add(this.listbox_attribute);
            this.groupbox2.location = new system.drawing.point(20, 349);
            this.groupbox2.name = "groupbox2";
            this.groupbox2.size = new system.drawing.size(898, 293);
            this.groupbox2.tabindex = 9;
            this.groupbox2.tabstop = false;
            this.groupbox2.text = "接收客户端消息";
            // 
            // lab_imgname
            // 
            this.lab_imgname.autosize = true;
            this.lab_imgname.location = new system.drawing.point(443, 265);
            this.lab_imgname.name = "lab_imgname";
            this.lab_imgname.size = new system.drawing.size(0, 12);
            this.lab_imgname.tabindex = 12;
            // 
            // picturebox1
            // 
            this.picturebox1.location = new system.drawing.point(440, 37);
            this.picturebox1.name = "picturebox1";
            this.picturebox1.size = new system.drawing.size(208, 220);
            this.picturebox1.sizemode = system.windows.forms.pictureboxsizemode.stretchimage;
            this.picturebox1.tabindex = 3;
            this.picturebox1.tabstop = false;
            // 
            // listbox_mes
            // 
            this.listbox_mes.formattingenabled = true;
            this.listbox_mes.itemheight = 12;
            this.listbox_mes.location = new system.drawing.point(6, 37);
            this.listbox_mes.name = "listbox_mes";
            this.listbox_mes.size = new system.drawing.size(428, 220);
            this.listbox_mes.tabindex = 2;
            // 
            // listbox_attribute
            // 
            this.listbox_attribute.formattingenabled = true;
            this.listbox_attribute.itemheight = 12;
            this.listbox_attribute.location = new system.drawing.point(654, 37);
            this.listbox_attribute.name = "listbox_attribute";
            this.listbox_attribute.size = new system.drawing.size(202, 220);
            this.listbox_attribute.tabindex = 1;
            // 
            // openfiledialog1
            // 
            this.openfiledialog1.filename = "openfiledialog1";
            // 
            // btn_stop
            // 
            this.btn_stop.location = new system.drawing.point(632, 8);
            this.btn_stop.name = "btn_stop";
            this.btn_stop.size = new system.drawing.size(75, 23);
            this.btn_stop.tabindex = 11;
            this.btn_stop.text = "关闭监听";
            this.btn_stop.usevisualstylebackcolor = true;
            this.btn_stop.click += new system.eventhandler(this.btn_stop_click);
            // 
            // label5
            // 
            this.label5.autosize = true;
            this.label5.location = new system.drawing.point(6, 17);
            this.label5.name = "label5";
            this.label5.size = new system.drawing.size(77, 12);
            this.label5.tabindex = 12;
            this.label5.text = "接收消息显示";
            // 
            // label6
            // 
            this.label6.autosize = true;
            this.label6.location = new system.drawing.point(438, 22);
            this.label6.name = "label6";
            this.label6.size = new system.drawing.size(77, 12);
            this.label6.tabindex = 13;
            this.label6.text = "接收图片显示";
            // 
            // label7
            // 
            this.label7.autosize = true;
            this.label7.location = new system.drawing.point(652, 22);
            this.label7.name = "label7";
            this.label7.size = new system.drawing.size(53, 12);
            this.label7.tabindex = 14;
            this.label7.text = "数据显示";
            // 
            // form1
            // 
            this.autoscaledimensions = new system.drawing.sizef(6f, 12f);
            this.autoscalemode = system.windows.forms.autoscalemode.font;
            this.clientsize = new system.drawing.size(930, 645);
            this.controls.add(this.btn_stop);
            this.controls.add(this.groupbox2);
            this.controls.add(this.groupbox1);
            this.controls.add(this.txt_monitor);
            this.controls.add(this.label3);
            this.controls.add(this.btn_startsocket);
            this.controls.add(this.txt_port);
            this.controls.add(this.label2);
            this.controls.add(this.txt_ip);
            this.controls.add(this.label1);
            this.name = "form1";
            this.text = "服务端";
            this.groupbox1.resumelayout(false);
            this.groupbox1.performlayout();
            ((system.componentmodel.isupportinitialize)(this.datagridview1)).endinit();
            this.groupbox2.resumelayout(false);
            this.groupbox2.performlayout();
            ((system.componentmodel.isupportinitialize)(this.picturebox1)).endinit();
            this.resumelayout(false);
            this.performlayout();

        }

        #endregion

        private system.windows.forms.label label1;
        private system.windows.forms.textbox txt_ip;
        private system.windows.forms.textbox txt_port;
        private system.windows.forms.label label2;
        private system.windows.forms.button btn_startsocket;
        private system.windows.forms.textbox txt_monitor;
        private system.windows.forms.label label3;
        private system.windows.forms.groupbox groupbox1;
        private system.windows.forms.button btn_sendshake;
        private system.windows.forms.button btn_sendmes;
        private system.windows.forms.textbox txt_mes;
        private system.windows.forms.label label4;
        private system.windows.forms.datagridview datagridview1;
        private system.windows.forms.groupbox groupbox2;
        private system.windows.forms.listbox listbox_attribute;
        private system.windows.forms.datagridviewtextboxcolumn column1;
        private system.windows.forms.datagridviewtextboxcolumn column2;
        private system.windows.forms.listbox listbox_mes;
        private system.windows.forms.picturebox picturebox1;
        private system.windows.forms.button btn_sendimg;
        private system.windows.forms.openfiledialog openfiledialog1;
        private system.windows.forms.label lab_imgname;
        private system.windows.forms.button btn_stop;
        private system.windows.forms.label label7;
        private system.windows.forms.label label6;
        private system.windows.forms.label label5;
    }
}
view code

运行代码:

using system;
using system.collections.generic;
using system.componentmodel;
using system.data;
using system.drawing;
using system.linq;
using system.text;
using system.threading.tasks;
using system.windows.forms;
using system.net.sockets;
using system.net;
using system.threading;
using system.drawing.imaging;
using system.io;
using system.collections;
using system.xml;

namespace socketservice
{
    public partial class form1 : form
    {
        public form1()
        {
            initializecomponent();
        }

        #region 参数声明
        /// <summary>
        /// 字典 ip加客户端状态
        /// </summary>
        static dictionary<string, clientstate> dic_ip = new dictionary<string, clientstate>();
        /// <summary>
        /// 服务端启动的socket
        /// </summary>
        socket s_socket;
        #endregion

        /// <summary>
        /// 开启监听
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_startsocket_click(object sender, eventargs e)
        {
            if (string.isnullorwhitespace(this.txt_ip.text.trim()) && string.isnullorwhitespace(this.txt_port.text.trim()) && !string.isnullorwhitespace(this.txt_monitor.text.trim()))
            {
                messagebox.show("输入不符合或已在监听");
                return;
            }

            //获取ip port异步连接socket
            string ip = this.txt_ip.text.trim();
            int port = int.parse(this.txt_port.text.trim());
            s_socket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
            try
            {
                s_socket.bind(new ipendpoint(ipaddress.parse(ip), port));
                s_socket.listen(1);//同时连接的最大连接数
                s_socket.beginaccept(new asynccallback(accept), s_socket);//获取连接

                this.txt_monitor.text = ip + ":" + port;
                this.txt_ip.enabled = false;
                this.txt_port.enabled = false;
            }
            catch (exception ex)
            {
                console.writeline("error ex=" + ex.message + " " + ex.stacktrace);
            }
        }

        /// <summary>
        /// 异步连接回调 获取请求socket 添加信息到控件
        /// </summary>
        /// <param name="ar"></param>
        private void accept(iasyncresult ar)
        {
            try
            {
                //获取连接socket 创建新的连接
                socket myserver = ar.asyncstate as socket;
                socket service = myserver.endaccept(ar);

                #region 内部逻辑 ui处理部分
                clientstate obj = new clientstate();
                obj.clientsocket = service;

                //添加到字典
                dic_ip.add(service.remoteendpoint.tostring(), obj);
                var point = service.remoteendpoint.tostring().split(':');
                this.begininvoke((methodinvoker)delegate ()
                {
                    //获取ip 端口添加到控件
                    int index = this.datagridview1.rows.add();
                    datagridview1.rows[index].cells[0].value = point[0];
                    datagridview1.rows[index].cells[1].value = point[1];
                });
                #endregion

                //接收连接socket数据
                service.beginreceive(obj.buffer, 0, clientstate.bufsize, socketflags.none, new asynccallback(readcallback), obj);
                myserver.beginaccept(new asynccallback(accept), myserver);//等待下一个连接
            }
            catch (exception ex)
            {
                console.writeline("服务端关闭"+ex.message+" "+ex.stacktrace);
            }
        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_sendmes_click(object sender, eventargs e)
        {
            try
            {
                var key = datagridview1.rows[datagridview1.currentrow.index].cells[0].value + ":" + datagridview1.rows[datagridview1.currentrow.index].cells[1].value;
                sendsocket send = new sendsocket() { message = this.txt_mes.text.trim() };
                send(dic_ip[key].clientsocket, send.toarray());
            }
            catch (exception ex)
            {
                messagebox.show("error ex=" + ex.message + " " + ex.stacktrace);
            }
        }

        /// <summary>
        /// 发送震动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_sendshake_click(object sender, eventargs e)
        {
            //根据选中的ip端口 获取对应客户端socket
            var key = datagridview1.rows[datagridview1.currentrow.index].cells[0].value + ":" + datagridview1.rows[datagridview1.currentrow.index].cells[1].value;
            if (dic_ip.containskey(key))
            {
                sendsocket send = new sendsocket()
                {
                    sendshake = true,
                    message = "震动",
                };
                send(dic_ip[key].clientsocket, send.toarray());
            }
            else
            {
                messagebox.show("选中数据无效,找不到客户端");
            }
        }

        /// <summary>
        /// 发送图片
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_sendimg_click(object sender, eventargs e)
        {
            var key = datagridview1.rows[datagridview1.currentrow.index].cells[0].value + ":" + datagridview1.rows[datagridview1.currentrow.index].cells[1].value;
            if (dic_ip.containskey(key))
            {
                //初始化一个openfiledialog类
                openfiledialog filedialog = new openfiledialog();

                //判断用户是否正确的选择了文件
                if (filedialog.showdialog() == dialogresult.ok)
                {
                    string extension = path.getextension(filedialog.filename);
                    string[] str = new string[] { ".gif", ".jpge", ".jpg", "bmp" };//准许上传格式
                    if (!((ilist)str).contains(extension))
                    {
                        messagebox.show("仅能上传gif,jpge,jpg,bmp格式的图片!");
                    }
                    fileinfo fileinfo = new fileinfo(filedialog.filename);
                    if (fileinfo.length > 26214400)//不能大于25 
                    {
                        messagebox.show("图片不能大于25m");
                    }

                    //将图片转成base64发送
                    sendsocket send = new sendsocket()
                    {
                        sendimg = true,
                        imgname = filedialog.safefilename,
                    };
                    using (filestream file = new filestream(filedialog.filename, filemode.open, fileaccess.read))
                    {
                        var imgby = new byte[file.length];
                        file.read(imgby, 0, imgby.length);
                        send.imgbase64 = convert.tobase64string(imgby);
                    }

                    send(dic_ip[key].clientsocket, send.toarray());
                }
            }
            else
            {
                messagebox.show("请正确选择,选中客户端不存在");
            }
        }

        #region socket 发送和接收
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="s_socket">指定客户端socket</param>
        /// <param name="message">发送消息</param>
        /// <param name="shake">发送消息</param>
        private void send(socket c_socket, byte[] by)
        {
            try
            {
                //发送
                c_socket.beginsend(by, 0, by.length, socketflags.none, asyncresult =>
                {
                    try
                    {
                        //完成消息发送
                        int len = c_socket.endsend(asyncresult);
                    }
                    catch (exception ex)
                    {
                        if (c_socket != null)
                        {
                            c_socket.close();
                            c_socket = null;
                        }
                        console.writeline("error ex=" + ex.message + " " + ex.stacktrace);
                    }
                }, null);
                this.txt_mes.text = null;
            }
            catch (exception ex)
            {
                if (c_socket != null)
                {
                    c_socket.close();
                    c_socket = null;
                }
                console.writeline("error ex=" + ex.message + " " + ex.stacktrace);
            }
        }

        /// <summary>
        /// 数据接收
        /// </summary>
        /// <param name="ar">请求的socket</param>
        private void readcallback(iasyncresult ar)
        {
            //获取并保存
            clientstate obj = ar.asyncstate as clientstate;
            socket c_socket = obj.clientsocket;
            try
            {
                int bytes = c_socket.endreceive(ar);
                #region 接收数据
                if (bytes == 16)
                {
                    byte[] buf = obj.buffer;
                    //判断头部是否正确 标识0-3 8888
                    if (buf[0] == 8 && buf[1] == 8 && buf[2] == 8 && buf[3] == 8)
                    {
                        //判断是否为震动 标识12-15 1111
                        if (buf[12] == 1 && buf[13] == 1 && buf[14] == 1 && buf[15] == 1)
                        {
                            //实现震动效果
                            this.begininvoke((methodinvoker)delegate ()
                            {
                                int x = 5, y = 10;
                                for (int i = 0; i < 5; i++)
                                {
                                    this.left += x;
                                    thread.sleep(y);
                                    this.top += x;
                                    thread.sleep(y);
                                    this.left -= x;
                                    thread.sleep(y);
                                    this.top -= x;
                                    thread.sleep(y);
                                }
                            });
                        }

                        else
                        {
                            int totallength = bitconverter.toint32(buf, 4);//获取数据总长度
                            //获取内容长度
                            int contentlength = bitconverter.toint32(buf, 8);
                            obj.buffer = new byte[contentlength];
                            int readdataptr = 0;
                            while (readdataptr < contentlength)//判断内容是否接收完成
                            {
                                var re = c_socket.receive(obj.buffer, readdataptr, contentlength - readdataptr, socketflags.none);//接收内容
                                readdataptr += re;
                            }

                            //转换显示 utf8
                            var str = encoding.utf8.getstring(obj.buffer, 0, contentlength);

                            if (buf[12] == 2 && buf[13] == 2 && buf[14] == 2 && buf[15] == 2)
                            {
                                #region 解析报文
                                //显示到listbox 
                                this.begininvoke((methodinvoker)delegate ()
                                {
                                    var time = datetime.now.tostring();
                                    this.listbox_mes.items.add(time + "   " + c_socket.remoteendpoint.tostring());
                                    this.listbox_mes.items.add("接收到图片");
                                    this.listbox_attribute.items.add(datetime.now.tostring());
                                    this.listbox_attribute.items.add("数据总长度 " + totallength + " 内容长度 " + contentlength);
                                });
                                try
                                {
                                    //解析xml 获取图片名称和base64字符串
                                    xmldocument document = new xmldocument();
                                    document.loadxml(str);

                                    xmlnodelist root = document.selectnodes("/imgmessage");
                                    string imgnmae = string.empty, imgbase64 = string.empty;
                                    foreach (xmlelement node in root)
                                    {
                                        imgnmae = node.getelementsbytagname("imgname")[0].innertext;
                                        imgbase64 = node.getelementsbytagname("imgbase64")[0].innertext;
                                    }

                                    //base64转成图片
                                    byte[] imgbuf = convert.frombase64string(imgbase64);
                                    using (system.io.memorystream m_str = new system.io.memorystream(imgbuf))
                                    {
                                        using (bitmap bit = new bitmap(m_str))
                                        {
                                            //保存到本地并上屏
                                            var path = path.combine(appdomain.currentdomain.basedirectory, imgnmae);
                                            bit.save(path);
                                            picturebox1.begininvoke((methodinvoker)delegate ()
                                            {
                                                lab_imgname.text = imgnmae;
                                                picturebox1.imagelocation = path;
                                            });
                                        }
                                    }
                                }
                                catch (exception ex)
                                {
                                    console.writeline(ex.message + " " + ex.stacktrace);
                                }
                                #endregion
                            }
                            else
                            {
                                //显示到listbox 
                                this.begininvoke((methodinvoker)delegate ()
                                {
                                    var time = datetime.now.tostring();
                                    this.listbox_mes.items.add(time + "   " + c_socket.remoteendpoint.tostring());
                                    this.listbox_mes.items.add(str);
                                    this.listbox_attribute.items.add(datetime.now.tostring());
                                    this.listbox_attribute.items.add("数据总长度 " + totallength + " 内容长度 " + contentlength);
                                });
                            }
                        }
                    }
                    //接收完成 重新给出buffer接收
                    obj.buffer = new byte[clientstate.bufsize];
                    c_socket.beginreceive(obj.buffer, 0, clientstate.bufsize, 0, new asynccallback(readcallback), obj);
                }
                else
                {
                    updatecontrols(c_socket);
                }
                #endregion
            }
            catch (exception ex)
            {
                updatecontrols(c_socket);
            }
        }

        /// <summary>
        /// 关闭指定客户端 更新控件
        /// </summary>
        /// <param name="socket"></param>
        public void updatecontrols(socket socket)
        {
            dic_ip.remove(socket.remoteendpoint.tostring());
            list<int> list = new list<int>();
            for (int i = 0; i < datagridview1.rowcount; i++)
            {

                var val = datagridview1.rows[i].cells[0].value + ":" + datagridview1.rows[i].cells[1].value;
                if (val != null && val.tostring() == socket.remoteendpoint.tostring())
                {
                    list.add(i);
                }
            }

            this.begininvoke((methodinvoker)delegate ()
            {
                foreach (var item in list)
                {
                    datagridview1.rows.remove(datagridview1.rows[item]);
                }
            });
            socket.close();
            socket.dispose();
        }
        #endregion

        /// <summary>
        /// 停止
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_stop_click(object sender, eventargs e)
        {
            s_socket.close();
            this.txt_ip.enabled = true;
            this.txt_port.enabled = true;
            this.txt_monitor.text = null;
        }
    }
}
view code

声明的类:

using system;
using system.collections.generic;
using system.linq;
using system.net.sockets;
using system.text;
using system.threading.tasks;

namespace socketservice
{
    /// <summary>
    /// 接收消息
    /// </summary>
    public class clientstate
    {
        public socket clientsocket = null;
        public const int bufsize = 16;
        public byte[] buffer = new byte[bufsize];
        public stringbuilder str = new stringbuilder();
    }

    /// <summary>
    /// 显示客户端ip 端口
    /// </summary>
    public class clientclass
    {
        public string ip { get; set; }
        public string port { get; set; }
    }

    /// <summary>
    /// 0-3 标识码 4-7 总长度 8-11 内容长度 12-15补0 16开始为内容
    /// </summary>
    public class sendsocket
    {
        /// <summary>
        /// 头 标识8888
        /// </summary>
        byte[] header = new byte[4] { 0x08, 0x08, 0x08, 0x08 };

        /// <summary>
        /// 文本消息
        /// </summary>
        public string message;

        /// <summary>
        /// 是否发送震动
        /// </summary>
        public bool sendshake = false;

        /// <summary>
        /// 是否发送图片
        /// </summary>
        public bool sendimg = false;

        /// <summary>
        /// 图片名称
        /// </summary>
        public string imgname;

        /// <summary>
        /// 图片数据
        /// </summary>
        public string imgbase64;
        /// <summary>
        /// 组成特定格式的byte数据
        /// 12-15 为指定发送内容 1111(震动) 2222(图片数据)
        /// </summary>
        /// <param name="mes">文本消息</param>
        /// <param name="shake">震动</param>
        /// <param name="img">图片</param>
        /// <returns>特定格式的byte</returns>
        public byte[] toarray()
        {
            if (sendimg)//是否发送图片
            {
                //组成xml接收 可以接收相关图片数据
                stringbuilder xmlresult = new stringbuilder("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
                xmlresult.append("<imgmessage>");
                xmlresult.appendformat("<imgname>{0}</imgname>", imgname);
                xmlresult.appendformat("<imgbase64>{0}</imgbase64>", imgbase64);
                xmlresult.append("</imgmessage>");
                message = xmlresult.tostring();
            }

            byte[] bytedata = encoding.utf8.getbytes(message);//内容
            int count = 16 + bytedata.length;//总长度
            byte[] sendby = new byte[count];
            array.copy(header, 0, sendby, 0, header.length);//添加头

            byte[] countby = bitconverter.getbytes(count);
            array.copy(countby, 0, sendby, 4, countby.length);//总长度

            byte[] contentby = bitconverter.getbytes(bytedata.length);
            array.copy(contentby, 0, sendby, 8, contentby.length);//内容长度

            if (sendshake)//发动震动
            {
                var shakeby = new byte[4] { 1, 1, 1, 1 };
                array.copy(shakeby, 0, sendby, 12, shakeby.length);//震动
            }

            if (sendimg)//发送图片
            {
                var imgby = new byte[4] { 2, 2, 2, 2 };
                array.copy(imgby, 0, sendby, 12, imgby.length);//图片
            }

            array.copy(bytedata, 0, sendby, 16, bytedata.length);//内容
            return sendby;
        }
    }
}
view code

客户端

窗体(ui)代码:

namespace socketclient
{
    partial class form1
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private system.componentmodel.icontainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.dispose();
            }
            base.dispose(disposing);
        }

        #region windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void initializecomponent()
        {
            this.btn_stopsocket = new system.windows.forms.button();
            this.txt_monitor = new system.windows.forms.textbox();
            this.label3 = new system.windows.forms.label();
            this.btn_startsocket = new system.windows.forms.button();
            this.txt_port = new system.windows.forms.textbox();
            this.label2 = new system.windows.forms.label();
            this.txt_ip = new system.windows.forms.textbox();
            this.label1 = new system.windows.forms.label();
            this.groupbox1 = new system.windows.forms.groupbox();
            this.picturebox1 = new system.windows.forms.picturebox();
            this.btn_sendimg = new system.windows.forms.button();
            this.btn_sendshake = new system.windows.forms.button();
            this.btn_sendmes = new system.windows.forms.button();
            this.label4 = new system.windows.forms.label();
            this.txt_mes = new system.windows.forms.textbox();
            this.listbox1 = new system.windows.forms.listbox();
            this.openfiledialog1 = new system.windows.forms.openfiledialog();
            this.lab_imgname = new system.windows.forms.label();
            this.groupbox1.suspendlayout();
            ((system.componentmodel.isupportinitialize)(this.picturebox1)).begininit();
            this.suspendlayout();
            // 
            // btn_stopsocket
            // 
            this.btn_stopsocket.location = new system.drawing.point(635, 10);
            this.btn_stopsocket.name = "btn_stopsocket";
            this.btn_stopsocket.size = new system.drawing.size(75, 23);
            this.btn_stopsocket.tabindex = 15;
            this.btn_stopsocket.text = "取消连接";
            this.btn_stopsocket.usevisualstylebackcolor = true;
            this.btn_stopsocket.click += new system.eventhandler(this.btn_stopsocket_click);
            // 
            // txt_monitor
            // 
            this.txt_monitor.location = new system.drawing.point(457, 14);
            this.txt_monitor.name = "txt_monitor";
            this.txt_monitor.readonly = true;
            this.txt_monitor.size = new system.drawing.size(144, 21);
            this.txt_monitor.tabindex = 14;
            // 
            // label3
            // 
            this.label3.autosize = true;
            this.label3.location = new system.drawing.point(398, 17);
            this.label3.name = "label3";
            this.label3.size = new system.drawing.size(53, 12);
            this.label3.tabindex = 13;
            this.label3.text = "正在连接";
            // 
            // btn_startsocket
            // 
            this.btn_startsocket.location = new system.drawing.point(307, 12);
            this.btn_startsocket.name = "btn_startsocket";
            this.btn_startsocket.size = new system.drawing.size(75, 23);
            this.btn_startsocket.tabindex = 12;
            this.btn_startsocket.text = "开始连接";
            this.btn_startsocket.usevisualstylebackcolor = true;
            this.btn_startsocket.click += new system.eventhandler(this.btn_startsocket_click);
            // 
            // txt_port
            // 
            this.txt_port.location = new system.drawing.point(183, 14);
            this.txt_port.name = "txt_port";
            this.txt_port.size = new system.drawing.size(100, 21);
            this.txt_port.tabindex = 11;
            this.txt_port.text = "9999";
            // 
            // label2
            // 
            this.label2.autosize = true;
            this.label2.location = new system.drawing.point(148, 19);
            this.label2.name = "label2";
            this.label2.size = new system.drawing.size(29, 12);
            this.label2.tabindex = 10;
            this.label2.text = "端口";
            // 
            // txt_ip
            // 
            this.txt_ip.location = new system.drawing.point(36, 14);
            this.txt_ip.name = "txt_ip";
            this.txt_ip.size = new system.drawing.size(100, 21);
            this.txt_ip.tabindex = 9;
            this.txt_ip.text = "127.0.0.1";
            // 
            // label1
            // 
            this.label1.autosize = true;
            this.label1.location = new system.drawing.point(13, 17);
            this.label1.name = "label1";
            this.label1.size = new system.drawing.size(17, 12);
            this.label1.tabindex = 8;
            this.label1.text = "ip";
            // 
            // groupbox1
            // 
            this.groupbox1.controls.add(this.lab_imgname);
            this.groupbox1.controls.add(this.picturebox1);
            this.groupbox1.controls.add(this.btn_sendimg);
            this.groupbox1.controls.add(this.btn_sendshake);
            this.groupbox1.controls.add(this.btn_sendmes);
            this.groupbox1.controls.add(this.label4);
            this.groupbox1.controls.add(this.txt_mes);
            this.groupbox1.controls.add(this.listbox1);
            this.groupbox1.location = new system.drawing.point(15, 48);
            this.groupbox1.name = "groupbox1";
            this.groupbox1.size = new system.drawing.size(939, 324);
            this.groupbox1.tabindex = 16;
            this.groupbox1.tabstop = false;
            this.groupbox1.text = "发送与接收";
            // 
            // picturebox1
            // 
            this.picturebox1.location = new system.drawing.point(346, 21);
            this.picturebox1.name = "picturebox1";
            this.picturebox1.size = new system.drawing.size(281, 280);
            this.picturebox1.sizemode = system.windows.forms.pictureboxsizemode.st


                    
                    

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网