欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-11 11:14:49
前言 看了一百遍,不如动手写一遍。 Socket这块使用不是特别熟悉,之前实现是公司有对应源码改改能用。 但是不理解实现的过程和步骤,然后最近有时间自己写个demo实现看看,熟悉熟悉Socket。 网上也有好的文章,结合别人的理接和自己实践总算写完了。。。 参考: https://www.cnblo ......

前言

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

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

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

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

参考:实现

参考:

         

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

实现过程

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

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

页面效果图:

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

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

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

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

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

C# 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连接

C# 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连接一样需再次执行获取下次数据

C# 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,消息发送比较简单,将发送的数组转成数组的形式进行发送

C# 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)
        {
               //发送
                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 在消息接收(可检测服务端断开),链接(链接失败)重新赋值然后判断是否为错误码然后重连

C# Socket异步实现消息发送--附带源码
        /// <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链接部分

C# Socket异步实现消息发送--附带源码
/// <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开始为内容

C# Socket异步实现消息发送--附带源码
/// <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)代码:

C# Socket异步实现消息发送--附带源码
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

运行代码:

C# Socket异步实现消息发送--附带源码
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

声明的类:

C# Socket异步实现消息发送--附带源码
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)代码:

C# Socket异步实现消息发送--附带源码
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

                    
                
(0)
打赏 C# Socket异步实现消息发送--附带源码 微信扫一扫

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

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