C# Socket异步实现消息发送--附带源码
前言
看了一百遍,不如动手写一遍。
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);//获取连接
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); } }
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); }
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); }
客户端实现(实现不包含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); } }
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); } }
整体数据发送和实现部分
声明一个指定的类,给出指定的标识,方便数据处理,
在发送文字消息,图片消息,震动时需要对应的判断其实最简便的方法是直接发送一个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; } }
代码:
服务端:
窗体(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; } }
运行代码:
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; } } }
声明的类:
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; } } }
客户端
窗体(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
相关文章:
-
-
本来计划接着上篇 C# Winform模仿百度日历,发现一时半会写不完,只写了一小半还不全,暂且搁置下。现在计划下班后每天至少写一篇博客,未能完成的... [阅读全文]
-
asp.net core系列 38 WebAPI 返回类型与响应格式--必备
一.返回类型 ASP.NET Core 提供以下 Web API Action方法返回类型选项,以及说明每种返回类型的最佳适用情况: (1) 固定类... [阅读全文] -
这篇博客记录@InitBinder怎么起作用、起什么作用? 首先,该注解被解析的时机,是该匹配Controller的请求执行映射的方法之前; 同时 ... [阅读全文]
-
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论