- 浏览: 2106582 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (1878)
- [网站分类]ASP.NET (141)
- [网站分类]C# (80)
- [随笔分类]NET知识库 (80)
- [随笔分类]摘抄文字[非技术] (3)
- [随笔分类]养生保健 (4)
- [网站分类]读书区 (16)
- [随笔分类]赚钱 (7)
- [网站分类].NET新手区 (233)
- [随笔分类]网站 (75)
- [网站分类]企业信息化其他 (4)
- [网站分类]首页候选区 (34)
- [网站分类]转载区 (12)
- [网站分类]SQL Server (16)
- [网站分类]程序人生 (7)
- [网站分类]WinForm (2)
- [随笔分类]错误集 (12)
- [网站分类]JavaScript (3)
- [随笔分类]小说九鼎记 (69)
- [随笔分类]技术文章 (15)
- [网站分类]求职面试 (3)
- [网站分类]其他技术区 (6)
- [网站分类]非技术区 (10)
- [发布至博客园首页] (5)
- [网站分类]jQuery (6)
- [网站分类].NET精华区 (6)
- [网站分类]Html/Css (10)
- [随笔分类]加速及SEO (10)
- [网站分类]Google开发 (4)
- [随笔分类]旅游备注 (2)
- [网站分类]架构设计 (3)
- [网站分类]Linux (23)
- [随笔分类]重要注册 (3)
- [随笔分类]Linux+PHP (10)
- [网站分类]PHP (11)
- [网站分类]VS2010 (2)
- [网站分类]CLR (1)
- [网站分类]C++ (1)
- [网站分类]ASP.NET MVC (2)
- [网站分类]项目与团队管理 (1)
- [随笔分类]个人总结 (1)
- [随笔分类]问题集 (3)
- [网站分类]代码与软件发布 (1)
- [网站分类]Android开发 (1)
- [网站分类]MySQL (1)
- [网站分类]开源研究 (6)
- ddd (0)
- 好久没写blog了 (0)
- sqlserver (2)
最新评论
-
JamesLiuX:
博主,能组个队么,我是Freelancer新手。
Freelancer.com(原GAF – GetAFreelancer)帐户里的钱如何取出? -
yw10260609:
我认为在混淆前,最好把相关代码备份一下比较好,不然项目完成后, ...
DotFuscator 小记 -
日月葬花魂:
大哥 能 加我个QQ 交流一下嘛 ?51264722 我Q ...
web应用程序和Web网站区别 -
iaimg:
我想问下嵌入delphi写的程序总是出现窗体后面感觉有个主窗体 ...
C#自定义控件:WinForm将其它应用程序窗体嵌入自己内部 -
iaimg:
代码地址下不了啊!
C#自定义控件:WinForm将其它应用程序窗体嵌入自己内部
本文讲下C#通过Socket编程实现平行主机之间网络通讯的详细讲解,非常不错的博文,一起来看下吧。
在程序设计中,涉及数据存储和数据交换的时候,不管是B/S还是C/S模式 ,都有这样一个概念:数据库服务器。这要求一台性能和配置都比较好的主机作为服务器,以满足数目众多的客户端进行频繁访问。但是对于一些数据交换的要求不主同,而且涉及到的通讯个体数目不多,如果还采用“一主机多客户机”的模式,便要求一台硬件配置良好而且软件上安装了相关数据服务软件,这样会造成硬件和软件上的很多不必要的成本,这时Socket在点对点的平行对象之间的网络通讯的优势就就发挥出来了。
其实对于Socket通讯来说,服务器和客户端的界定不像数据库服务器与客户端那样明显,甚至可以说Socket通讯里面的服务器和客户端只是相对的,因为网络通讯的对象基本上是处于平等层面的,只是为了方便对两台联网通讯的主机的描述才这样定义称谓的。
由于在.NET中Socket通讯的建立很容易,所以本文主要介绍一个Socket的比较典型的应用的流程:客户端向服务器发送图片请求,图片服务器接收到请求,并将服务器硬盘上的图片编码,发送到客户端,客户端得到图片数据后,再将这些数据写成图片文件,保存在客户端上。
本文主要是对Socket的一个应用进行介绍,所以至于其原理在此没有深究,至于如何建立Socket还有如何实现网络的七层协议在此都没有进行相关研究和介绍,本文主要介绍如何实现一个用户想要的功能,即在两台主机之间进行通讯,通过网络来收发用户想要收发的数据。
一、通讯流程图
二、通讯相关的代码
本文以Windows控制台程序为例来实现引功能。
不管是通讯服务器或者通讯客户端,本文均以一个不断运行的线程来实现对端口的侦听,将通讯相关的变量的函数做成一个类,在Program.cs中只负责初始化一些参数,然后建立通讯的线程。具体代码如下:
2.1服务器端
Program.cs:
using System;using System.Net;using System.Net.Sockets;using System.Threading;namespace ConsoleSocketsDemo{ class Program { static void Main(string[] args) { int sendPicPort = 600;//发送图片的端口 int recvCmdPort = 400;//接收请求的端口开启后就一直进行侦听 SocketServer socketServerProcess = new SocketServer(recvCmdPort, sendPicPort); Thread tSocketServer = new Thread(new ThreadStart(socketServerProcess.thread));//线程开始的时候要调用的方法为threadProc.thread tSocketServer.IsBackground = true;//设置IsBackground=true,后台线程会自动根据主线程的销毁而销毁 tSocketServer.Start(); Console.ReadKey();//直接main里边最后加个Console.Read()不就好了。要按键才退出。 } }}
SocketServer.cs:
using System;using System.Text;using System.Net;using System.Net.Sockets;using System.IO;namespace ConsoleSocketsDemo{ class SocketServer { Socket sRecvCmd; int recvCmdPort;//接收图片请求命令 int sendPicPort;//发送图片命令 public SocketServer(int recvPort,int sendPort) { recvCmdPort = recvPort; sendPicPort = sendPort; //建立本地socket,一直对4000端口进行侦听 IPEndPoint recvCmdLocalEndPoint = new IPEndPoint(IPAddress.Any, recvCmdPort); sRecvCmd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sRecvCmd.Bind(recvCmdLocalEndPoint); sRecvCmd.Listen(100); } public void thread() { while (true) { System.Threading.Thread.Sleep(1);//每个线程内部的死循环里面都要加个“短时间”睡眠,使得线程占用资源得到及时释放 try { Socket sRecvCmdTemp = sRecvCmd.Accept();//Accept 以同步方式从侦听套接字的连接请求队列中提取第一个挂起的连接请求,然后创建并返回新的 Socket sRecvCmdTemp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 5000);//设置接收数据超时 sRecvCmdTemp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 5000);//设置发送数据超时 sRecvCmdTemp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, 1024); //设置发送缓冲区大小 1K sRecvCmdTemp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 1024);//设置接收缓冲区大小1K byte[] recvBytes = new byte[1024];//开启一个缓冲区,存储接收到的信息 sRecvCmdTemp.Receive(recvBytes); //将读得的内容放在recvBytes中 string strRecvCmd = Encoding.Default.GetString(recvBytes);// //程序运行到这个地方,已经能接收到远程发过来的命令了 //************* //解码命令,并执行相应的操作----如下面的发送本机图片 //************* string[] strArray = strRecvCmd.Split(';'); if (strArray[0] == "PicRequest") { string[] strRemoteEndPoint = sRecvCmdTemp.RemoteEndPoint.ToString().Split(':');//远处终端的请求端IP和端口,如:127.0.0.1:4000 string strRemoteIP = strRemoteEndPoint[0]; SentPictures(strRemoteIP, sendPicPort); //发送本机图片文件 recvBytes = null; } } catch(Exception ex) { Console.Write(ex.Message); } } } /// <summary> /// 向远程客户端发送图片 /// </summary> /// <param name="strRemoteIP">远程客户端IP</param> /// <param name="sendPort">发送图片的端口</param> private static void SentPictures(string strRemoteIP, int sendPort) { string path = "D:\\images\\"; string strImageTag = "image";//图片名称中包含有image的所有图片文件 try { string[] picFiles = Directory.GetFiles(path, strImageTag + "*", SearchOption.TopDirectoryOnly);//满足要求的文件个数 if (picFiles.Length == 0) { return;//没有图片,不做处理 } long sendBytesTotalCounts = 0;//发送数据流总长度 //消息头部:命令标识+文件数目+……文件i长度+ string strMsgHead = "PicResponse;" + picFiles.Length + ";"; //消息体:图片文件流 byte[][] msgPicBytes = new byte[picFiles.Length][]; for (int j = 0; j < picFiles.Length; j++) { FileStream fs = new FileStream(picFiles[j].ToString(), FileMode.Open, FileAccess.Read); BinaryReader reader = new BinaryReader(fs); msgPicBytes[j] = new byte[fs.Length]; strMsgHead += fs.Length.ToString() + ";"; sendBytesTotalCounts += fs.Length; reader.Read(msgPicBytes[j], 0, msgPicBytes[j].Length); } byte[] msgHeadBytes = Encoding.Default.GetBytes(strMsgHead);//将消息头字符串转成byte数组 sendBytesTotalCounts += msgHeadBytes.Length; //要发送的数据流:数据头+数据体 byte[] sendMsgBytes = new byte[sendBytesTotalCounts];//要发送的总数组 for (int i = 0; i < msgHeadBytes.Length; i++) { sendMsgBytes[i] = msgHeadBytes[i]; //数据头 } int index = msgHeadBytes.Length; for (int i = 0; i < picFiles.Length; i++) { for (int j = 0; j < msgPicBytes[i].Length; j++) { sendMsgBytes[index + j] = msgPicBytes[i][j]; } index += msgPicBytes[i].Length; } //程序执行到此处,带有图片信息的报文已经准备好了 //PicResponse;2;94223;69228; //+图片1比特流+……图片2比特流 try { #region 发送图片 Socket sSendPic = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ipAddress = IPAddress.Parse(strRemoteIP);//remoteip = "127.0.0.1" try { sSendPic.Connect(ipAddress, sendPort);//连接无端客户端主机 sSendPic.Send(sendMsgBytes, sendMsgBytes.Length, 0);//发送本地图片 } catch (System.Exception e) { System.Console.Write("SentPictures函数在建立远程连接时出现异常:" + e.Message); }finally { sSendPic.Close(); } #endregion } catch { } } catch(Exception ex) { Console.Write(ex.Message); } } }}
2.2客户端端
Program.cs:
using System;using System.Text;using System.Net;using System.Net.Sockets;using System.Threading;namespace ConsoleClientSocketDemo{ class Program { static void Main(string[] args) { int recvPort = 600;//客户端一直对600端口进行侦听---接收图片的端口 RecvPic recvPic = new RecvPic(recvPort);//监听接收来自图片服务器的图片以及客户端的命令 Thread tRecvPic = new Thread(new ThreadStart(recvPic.thread)); tRecvPic.IsBackground = true; tRecvPic.Start(); string strPicServerIP = "127.0.0.1";//图片服务器的IP----127.0.0.1(localhost)--以本机为例 int sendRequestPort = 400;//发送图片请求的端口 SendStrMsg(strPicServerIP, sendRequestPort); Console.ReadKey();//直接main里边最后加个Console.Read()不就好了。要按键才退出。 } /// <summary> /// 向目标主机发送字符串 请求图片 /// </summary> /// <param name="strPicServerIP">目标图片服务器IP</param> /// <param name="sendRequestPort">目标图片服务器接收请求的端口</param> private static void SendStrMsg(string strPicServerIP, int sendRequestPort) { //可以在字符串编码上做文章,可以传送各种信息内容,目前主要有三种编码方式: //1.自定义连接字符串编码--微量 //2.JSON编码--轻量 //3.XML编码--重量 string strPicRequest = "PicRequest;Hello world,need some pictures~!";//图片请求 IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(strPicServerIP.ToString()), sendRequestPort); Socket answerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { answerSocket.Connect(ipEndPoint);//建立Socket连接 byte[] sendContents = Encoding.UTF8.GetBytes(strPicRequest); answerSocket.Send(sendContents, sendContents.Length, 0);//发送二进制数据 } catch (Exception ex) { Console.Write(ex.Message); } finally { answerSocket.Close(); } } }}RecvPic.cs:
using System;using System.Text;using System.Net;using System.Net.Sockets;using System.IO;namespace ConsoleClientSocketDemo{ class RecvPic { Socket sRecvPic;//接收图片的socket int recvPicPort;//接收图片端口 public RecvPic(int recvPort) { recvPicPort = recvPort; IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, recvPicPort); sRecvPic = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sRecvPic.Bind(localEndPoint); sRecvPic.Listen(100); } public void thread() { while (true) { System.Threading.Thread.Sleep(1);//每个线程内部的死循环里面都要加个“短时间”睡眠,使得线程占用资源得到及时释放 try { Socket sRecvPicTemp = sRecvPic.Accept();//一直在等待socket请求,并建立一个和请求相同的socket,覆盖掉原来的socket sRecvPicTemp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 5000); //设置接收数据超时 sRecvPicTemp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 5000);//设置发送数据超时 sRecvPicTemp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, 1024);//设置发送缓冲区大小--1K大小 sRecvPicTemp.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, 1024); //设置接收缓冲区大小 #region 先取出数据头部信息---并解析头部 byte[] recvHeadBytes = new byte[1024];//先取1K的数据,提取出数据的头部 sRecvPicTemp.Receive(recvHeadBytes, recvHeadBytes.Length, 0); string recvStr = Encoding.UTF8.GetString(recvHeadBytes); string[] strHeadArray = recvStr.Split(';');//PicResponse;2;94223;69228; string strHeadCmd = strHeadArray[0];//头部命令 int picCounts = Convert.ToInt32(strHeadArray[1]) ;//数据流中包含的图片个数 int[] picLength=new int[picCounts];//每个图片的长度 for (int i = 0; i < picCounts;i++ ) { picLength[i] = Convert.ToInt32(strHeadArray[i+2]); } #endregion int offset=0;//数据头的长度 for (int k = 0; k < strHeadArray.Length - 1;k++ ) { offset += strHeadArray[k].Length + 1;//因为后面的分号 } int picOffset = recvHeadBytes.Length - offset;//第一张图片在提取数据头的时候已经被提取了一部分了 if (strHeadCmd == "PicResponse") { #region 储存图片--为了节约内存,可以每接收一次就保存一次图片 for (int i = 0; i < picCounts; i++) { byte[] recvPicBytes = new byte[(picLength[i])];//每次只接收一张图片 if (i == 0)//第一幅图片有一部分在提取数据头的时候已经提取过了。 { byte[] recvFirstPicBuffer = new byte[picLength[i] - picOffset]; sRecvPicTemp.Receive(recvFirstPicBuffer, recvFirstPicBuffer.Length, 0); for (int j = 0; j < picOffset; j++) { recvPicBytes[j] = recvHeadBytes[offset + j];//第一幅图片的前一部分 } for (int j = 0; j < recvFirstPicBuffer.Length; j++)//第一张图片的后半部分 { recvPicBytes[picOffset + j] = recvFirstPicBuffer[j]; } //将图片写入文件 SavePicture(recvPicBytes, "-0"); } else { sRecvPicTemp.Receive(recvPicBytes, recvPicBytes.Length, 0);//每次取一张图片的长度 SavePicture(recvPicBytes, "-"+i.ToString()); //将图片数据写入文件 } } #endregion } } catch(Exception ex) { Console.Write(ex.Message); } finally { } } } /// <summary> /// 保存图片到指定路径 /// </summary> /// <param name="picBytes">图片比特流</param> /// <param name="picNum">图片编号</param> public void SavePicture(byte[] picBytes, string picNum) { string filename = "receivePic"; if (!Directory.Exists("E:\\images\\")) Directory.CreateDirectory("E:\\images\\"); if (File.Exists("E:\\images\\" + filename + picNum + ".jpg")) return; FileStream fs = new FileStream("E:\\images\\" + filename + picNum + ".jpg", FileMode.OpenOrCreate, FileAccess.Write); fs.Write(picBytes, 0, picBytes.Length); fs.Dispose(); fs.Close(); } }}
三、测试socket的连接方法,telnet远程登录
用户可以同时对客户端和服务器端的Socket程序进行编写,然后进行联调,也可以一次只编写一个,然后通过下面的方法来测试Socket连接。
一般通过远程登录来测试连接是否成功,比如测试本机的400端口是否能连接成功:
“运行->cmd->telnet 127.0.0.1 400”
在没有运行对本机的400端口进行不断侦听的程序时,会出现连接失败的提示:
如果连接成功,则会弹出另外一个窗口:
如果在侦听线程里面设置断点,通常连接成功后,就会在
Socket sRecvCmdTemp = sRecvCmd.Accept();
之后的语句上断点。
附件:SocketDemo.rar
附近演示程序的说明:
1.使用VS2005创建。
2.主要实现的功能是:主机A向主机B发图片请求,主机B将D盘image目录下的image0.jpg,image1.jpg文件编码发送到主机B,主机B再解码并写成图片文件到E盘的image目录下。
3.为了方便调试,演示程序将服务器和客户端同时放在本机上,即localhost或者127.0.0.1,即本程序最终实现的效果就是将本机的D盘image目录下的两个指定名称的图片传送到E盘image目录下。所以在运行本程序前,先在D:/image目录下放置两张命名为image0.jpg,image1.jpg的图片文件
4.先运行服务器程序,再运行客户端程序
相关推荐
C#.Net开发Socket 知识,例程;C# Socket编程笔记,接收发送数据示例代码,适合初学者学习
C# 版socket 收发TCP协议有模版样例
C#.net同步异步SOCKET通讯和多线程总结~
1.基于HPSocket实现socket通讯, 实现断线自动重连、收发送消息、上线和下线提醒等功能。 2. 资源包含C#演示程序和C++dll
最近一个项目因为要用到Socket传输问题,所以决定学习一下,将自己学习的内容总结分享出来,下面这篇文章主要给大家介绍了关于C# .NET中Socket简单实用框架使用的相关资料,文中通过示例代码介绍的非常详细,需要的...
该压缩包中包含 (1).Net Socket编程基础;(2).NET 中 System.Diagnostics....(8)在C#中使用异步Socket编程实现TCP网络服务的CS的通讯构架;(9)重新研究socket传输自定义对象 9个doc帮助文档,已经两个Demo程序.请下载学习
本人通过网络搜索,搜索到的一个感觉蛮不错的socket通讯demo示例。希望对想学习C#网络编程的朋友有所帮助! 对于想开发网络通讯程序的朋友具有不错的参考价值!
在C#中使用异步Socket编程实现TCP网络服务的CS的通讯构架.
管理系统 mvc asp.net c# demo 示例 代码
C#.Net Socket网络聊天室编程实例附教程C#.Net Socket网络聊天室编程实例附教程C#.Net Socket网络聊天室编程实例附教程
C# Socket Socket Socket Socket 发送与接收
WPF实现CS结构,基于socket连接,实现Server<->Client发送消息
C#利用Socket实现客户端之间直接通信 实验功能: 设计程序,分别构建通信的两端:服务器端和客户端应用程序,套接字类型为面向连接的Socket,自己构建双方的应答模式,实现双方的数据的发送和接收(S发给C,C发给S)...
C#socket 大文件传输 分段传输 C#DEMO
C#.net同步异步SOCKET通讯和多线程总结2[参照].pdf
Visual C# .NET 网络核心编程
c# socket tcp 通信案例 c# socket tcp 通信案例 c# socket tcp 通信案例
C# Socket通讯/TCP通讯,完整代码demo
c# socket编程 一个简单的模拟QQ聊天软件 可以群聊