小兵传奇三:scoket网络编程传输图片
2013-07-15 10:06
405 查看
做一个传输图片的的scoket,遇到了各种各样的问题(附录中给出)
后来用一个比较稳定的框架后得到解决
网络框架服务端:http://download.csdn.net/detail/q383965374/5744159
这里记录该网络框架的用法:
整个传输图片的流程:
打开客户端时 客户端中用新线程打开netinputwork方法监听服务端的信号。
在客户端发送图片时,用startsend方法创建文件流打开文件流,方法内调用了ProcSendMsg方法把图片大小和要在服务端要存的路径,图片保存的名字这三个信息发送给服务端,并用10信号说明是一张图片的首次发送。
向服务端发射10信号,服务端接到10信号后创建相应的文件夹及文件流,并返回11信号
客户端监听到11信号后
先判断当前图片位置加上4096位移是不是已经超过图片大小,如果是的话就说明这是一张图片的最后一次发送,把信号设为30,并发送最后的字节.服务端接到30信号后就知道这已经是一张图片的末尾了,处理完后对客户端发送21信号。客户端收到21信号后,把线程的堵塞打开,则开始传一下张图片
如果不是最后一张图片的末尾 则继续发送 用20信号,服务端接受到20信号只做写入文件流的操作
服务端中的接收类:
FileServer.cs代码
根据服务端的需求的操作 只需要修改 服务端的逻辑处理中的 代码就行了 这里实现的 是 接收客户端发送的信息 把图片写入 相应的 文件夹。
客户端要引入服务端中的两个project-------CommonLib和SANetworkEngine
然后新建一个发送类
客户端中的发送类:
客户端要修改操作时,主要是修改NetInputWork() ,Startsend() 还有ProSendMsg() ProSendMsg1()这几个函数。
在需要发送的地方调用发送类中的方法:
在主窗体Form的登录函数添加连接:
Form1_Load(object sender, EventArgs e) 函数的相应位置添加:
并且增加以下函数:
附录:
现象:服务端打开但是过了一会就关闭了一个连接,或者远程主机关闭了一个连接
原因1:
接收的
监听的队列不够
解决方法:
1.增加监听的队列
2.把监听放到新线程中
原因2:
服务端在接受数据时,客户端突然关闭或者 因为网络原因连接关闭
解决方法:
接收数据应该放在try catch中,使其不影响到下面的传输 ,不应作为一个异常处理
解决方法3:
用阻塞单线程,一个传完了再运行另一个(框架中的方法)
现象:接受完后有些文件或者图片不全
原因:
Arithmetic operation resulted in an overflo scoket发生算术溢出或者算术异常
soket缓冲区只有8k字节,当每次发送的字节太多而网速又不够时 就会发生上面这两种错误
解决方法有两种
1.将发送后的线程等待时间 thread.sleep设得长一点.
2.比较安全的方法:不要一次把整个文件发送,而是分段发送,而且每段的字节不超过8字节 也就是 8x1024 byte (一般设置为2x1024byte或者 4x1024byte)
后来用一个比较稳定的框架后得到解决
网络框架服务端:http://download.csdn.net/detail/q383965374/5744159
这里记录该网络框架的用法:
整个传输图片的流程:
打开客户端时 客户端中用新线程打开netinputwork方法监听服务端的信号。
在客户端发送图片时,用startsend方法创建文件流打开文件流,方法内调用了ProcSendMsg方法把图片大小和要在服务端要存的路径,图片保存的名字这三个信息发送给服务端,并用10信号说明是一张图片的首次发送。
向服务端发射10信号,服务端接到10信号后创建相应的文件夹及文件流,并返回11信号
客户端监听到11信号后
先判断当前图片位置加上4096位移是不是已经超过图片大小,如果是的话就说明这是一张图片的最后一次发送,把信号设为30,并发送最后的字节.服务端接到30信号后就知道这已经是一张图片的末尾了,处理完后对客户端发送21信号。客户端收到21信号后,把线程的堵塞打开,则开始传一下张图片
如果不是最后一张图片的末尾 则继续发送 用20信号,服务端接受到20信号只做写入文件流的操作
服务端中的接收类:
FileServer.cs代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SANetworkEngine; using System.IO; using System.Runtime.InteropServices; namespace FileTransServer { public class FileServer { private ServerHost m_host; public void Start() { m_host = new ServerHost(true, CallbackThreadType.IOThread, new FileService(), 2048, 15000, 180000, 2000); m_host.AddListener(new System.Net.IPEndPoint(System.Net.IPAddress.Any,8500), 100, 3); try { m_host.Start(); Console.WriteLine(DateTime.Now.ToString() + ">服务启动成功\r\n"); } catch (Exception ex) { Console.WriteLine(DateTime.Now.ToString() + ">服务启动失败:" + ex.Message + "\r\n"); } } } // 服务器处理逻辑 public class FileService : BaseService { // private string save = AppDomain.CurrentDomain.BaseDirectory; private string save = @"D:\TEST"; class FileClientInfo { public string FileName; public int FileSize; public int FileOffset; public string FileDir; public FileStream FS; } private Dictionary<int, FileClientInfo> fileList = new Dictionary<int, FileClientInfo>(); public override void OnConnected(ConnectionEventArgs e) { Console.WriteLine("有客户上线:"+e.Connection.HashId); fileList.Add(e.Connection.HashId, new FileClientInfo()); base.OnConnected(e); } public override void OnDisconnected(ConnectionEventArgs e) { base.OnDisconnected(e); } /// <summary> /// 服务器的逻辑处理 /// 第1阶段设置的服务器 只包含主要的功能 /// 1:验证登陆 2:接受发送的预约成功的消息并记录 /// 下来,准备的得到信息就是钱 /// 2:阶段添加更多的可能的控制功能 /// </summary> /// <param name="e"></param> int count = 0; public override void OnReceived(MessageEventArgs e) { // 0xff, 0xaa, 0xff if (e.Buffer.Length == 3 && e.Buffer[0] == 0xff && e.Buffer[1] == 0xaa && e.Buffer[2] == 0xff) // 心跳包不压缩 { base.OnReceived(e); return; // } try { byte[] info = CommonLib.Util.SecTool.SecDecode(e.Buffer); byte[] copyBuf=new byte[info.Length-1]; int cmd = info[0]; List<byte> rtData = new List<byte>(); switch (cmd) { case 10: // 获得文件信息 string data= Encoding.UTF8.GetString(info, 1, info.Length - 1); string[] datasp = data.Split(new char[] { ',' }); fileList[e.Connection.HashId].FileName =datasp[0]; fileList[e.Connection.HashId].FileSize = int.Parse(datasp[1]); fileList[e.Connection.HashId].FileDir = datasp[2]; fileList[e.Connection.HashId].FileOffset = 0; string dir=Path.Combine(save,fileList[e.Connection.HashId].FileDir); if(!Directory.Exists(dir)) { try{ Directory.CreateDirectory(dir); }catch{} } fileList[e.Connection.HashId].FS = new FileStream(Path.Combine(dir, fileList[e.Connection.HashId].FileName), FileMode.Create); rtData.Add((byte)11); e.Connection.BeginSend(CommonLib.Util.SecTool.SecCode(rtData.ToArray())); break; case 20: try { Buffer.BlockCopy(info, 1, copyBuf, 0, copyBuf.Length); fileList[e.Connection.HashId].FS.Write(copyBuf, 0 , copyBuf.Length); //fileList[e.Connection.HashId].FileOffset += copyBuf.Length; } catch { Console.Write("写入错误"); } rtData.Add((byte)11); e.Connection.BeginSend(CommonLib.Util.SecTool.SecCode(rtData.ToArray())); break; case 30: try { Buffer.BlockCopy(info, 1, copyBuf, 0, copyBuf.Length); fileList[e.Connection.HashId].FS.Write(copyBuf, 0 , copyBuf.Length); //fileList[e.Connection.HashId].FileOffset += copyBuf.Length; } catch { Console.Write("写入错误"); } rtData.Add((byte)21); e.Connection.BeginSend(CommonLib.Util.SecTool.SecCode(rtData.ToArray())); fileList[e.Connection.HashId].FS.Close(); count++; Console.Write("传完一张\t" + DateTime.Now+"\t"+count+"\n"); break; } } catch { Console.Write("接受错误"); } base.OnReceived(e); } public override void OnException(ExceptionEventArgs e) { if (e.Exception != null) { } base.OnException(e); } public override void OnSend(MessageEventArgs e) { base.OnSend(e); } } }
根据服务端的需求的操作 只需要修改 服务端的逻辑处理中的 代码就行了 这里实现的 是 接收客户端发送的信息 把图片写入 相应的 文件夹。
客户端要引入服务端中的两个project-------CommonLib和SANetworkEngine
然后新建一个发送类
客户端中的发送类:
using System.Threading; using Import_Advtisement; using System.IO; using System.Collections.Generic; using System.Text; public class FileClient: SANetworkEngine.BaseDisposable { static FileClient m_inst; private string m_connectIP; public string ConnectIP { get { return m_connectIP; } set { m_connectIP = value; } } private int m_connectPort; public int ConnectPort { get { return m_connectPort; } set { m_connectPort = value; } } private SANetworkEngine.SimpleClient m_net; public SANetworkEngine.SimpleClient Net { get { return m_net; } set { m_net = value; } } Thread m_netInputWork; bool m_netInputFlag; private Form1 m_uiHandler; public Form1 UiHandler { get { return m_uiHandler; } set { m_uiHandler = value; } } private FileClient() { m_netInputWork = new Thread(new ThreadStart(NetInputWork)); m_netInputWork.IsBackground = true; } public static FileClient Instance { get { if (m_inst == null) { lock (typeof(FileClient)) { if (m_inst == null) { m_inst = new FileClient(); } } } return m_inst; } } private void NetInputWork() { while (m_netInputFlag) { // 目前只有一个回应消息处理 byte[] info = m_net.Read(); if (info != null) { try { // 读一个字节的指令 // 解码 info = CommonLib.Util.SecTool.SecDecode(info); int cmd = info[0]; int tran_size=4096; byte cmdd = 20; switch (cmd) { case 11: if (file_offer + 4096 >= file_size) { tran_size = file_size - file_offer; cmdd = 30; } byte[] buffer = new byte[tran_size]; file_stream.Read(buffer, 0,tran_size); file_offer += tran_size; ProcSendMsg1(cmdd,buffer); break; case 21: file_stream.Close(); reset_event.Set(); break; } } catch { int a = 4; } } else if (m_net.LastError != null) { int a = 4; } else { int a = 4; } Thread.Sleep(10); } } private void ProcSendMsg1(byte cmd, byte[] data_pic) { List<byte> data = new List<byte>(); data.Add(cmd); data.AddRange(data_pic); byte[] msg = data.ToArray(); msg = CommonLib.Util.SecTool.SecCode(msg); m_net.Write(msg); // 发送网络信息 } /// <summary> /// 启动网络获取器 /// </summary> public void StartNetGet() { m_netInputFlag = true; m_netInputWork.Start(); } public void ResetClient() { Free(true); m_netInputWork = new Thread(new ThreadStart(NetInputWork)); m_netInputWork.IsBackground = true; } protected override void Free(bool canAccessFinalizable) { m_net.Disconnect(); m_net.Dispose(); // 释放网络资源 m_netInputFlag = false; //try //{ // m_netInputWork.Abort(); //} //catch { } base.Free(canAccessFinalizable); } int file_offer; int file_size; FileStream file_stream; public void StartSend(string strFileName, string strDictory, string strSaveFileName) { //创建一个文件对象 file_offer = 0; FileInfo EzoneFile = new FileInfo(strFileName); //打开文件流 file_stream = EzoneFile.OpenRead(); file_size = (int)file_stream.Length; ProcSendMsg(10, strSaveFileName+","+file_size+","+strDictory); reset_event.WaitOne(); } private AutoResetEvent reset_event=new AutoResetEvent(false); //false为激活状态 // 就采用一个字节作为指令有0-255 种 private void ProcSendMsg(byte cmd, string info) { List<byte> data = new List<byte>(); data.Add(cmd); data.AddRange(Encoding.UTF8.GetBytes(info)); byte[] msg = data.ToArray(); msg = CommonLib.Util.SecTool.SecCode(msg); m_net.Write(msg); // 发送网络信息 } //public void SendCodeBitmap(string codemark, byte[] imgData) //{ // List<byte> data = new List<byte>(); // data.Add((byte)80); // byte[] mark = Encoding.UTF8.GetBytes(codemark); // short marklen = (short)mark.Length; // data.AddRange(BitConverter.GetBytes(marklen)); // data.AddRange(mark); // data.AddRange(imgData); // byte[] msg = data.ToArray(); // msg = CommonLib.Util.SecTool.SecCode(msg); // m_net.Write(msg); // 发送网络信息 //} //// 登陆信息发送 //public void SendLoginInfo(string id, string logname,string proxysn) //{ // ProcSendMsg((byte)10, id + "," + logname + "," + proxysn); //} //public void SendAcctInfo(string pid, string pwd, string proxysn) //{ // ProcSendMsg((byte)20, pid + "," + pwd + "," + proxysn); //} //public void SendSuccessInfo(string pid, string proxysn) //{ // ProcSendMsg((byte)30, pid + "," + proxysn); //} }
客户端要修改操作时,主要是修改NetInputWork() ,Startsend() 还有ProSendMsg() ProSendMsg1()这几个函数。
在需要发送的地方调用发送类中的方法:
FileClient.Instance.StartSend(directoryName + "\\" + dt.Rows[i]["月"].ToString() + "." + dt.Rows[i]["日"].ToString() + "\\" + dt.Rows[i]["媒体"].ToString() + "\\" + sValue, Convert.ToDateTime(dt.Rows[i]["发布日期"].ToString()).ToString("yyyy-MM"), fileName + sValue.Substring(sValue.LastIndexOf(".")));
在主窗体Form的登录函数添加连接:
Form1_Load(object sender, EventArgs e) 函数的相应位置添加:
using System.Net; using System.Net.Sockets;
FileClient.Instance.UiHandler = this; FileClient.Instance.Net = new SANetworkEngine.SimpleClient(true, SANetworkEngine.CallbackThreadType.IOThread, new IPEndPoint(IPAddress.Parse("192.168.0.79"), 8500), 2048, 3, 3000, true, 15000); // new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8500), 2048, 3, 3000, true, 15000); FileClient.Instance.Net.OnHeart += new EventHandler<SANetworkEngine.HeartEvent>(Net_OnHeart); FileClient.Instance.Net.OnException += new EventHandler<SANetworkEngine.ClientExceptionEvent>(Net_OnException); FileClient.Instance.Net.OnDisconnect += new EventHandler(Net_OnDisconnect); FileClient.Instance.Net.ConnectServer(); if (FileClient.Instance.Net.Connected) { FileClient.Instance.StartNetGet(); } else { MessageBox.Show("无法连接"); }
并且增加以下函数:
void Net_OnDisconnect(object sender, EventArgs e) { FileClient.Instance.ResetClient(); FileClient.Instance.Net = new SANetworkEngine.SimpleClient(true, SANetworkEngine.CallbackThreadType.IOThread, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8500), 2048, 3, 3000, true, 15000); FileClient.Instance.Net.OnHeart += new EventHandler<SANetworkEngine.HeartEvent>(Net_OnHeart); FileClient.Instance.Net.OnException += new EventHandler<SANetworkEngine.ClientExceptionEvent>(Net_OnException); FileClient.Instance.Net.OnDisconnect += new EventHandler(Net_OnDisconnect); FileClient.Instance.Net.ConnectServer(); if (FileClient.Instance.Net.Connected) { FileClient.Instance.StartNetGet(); } else { } } void Net_OnException(object sender, SANetworkEngine.ClientExceptionEvent e) { throw new NotImplementedException(); } private byte[] heartdata = new byte[] { 0xff, 0xaa, 0xff }; void Net_OnHeart(object sender, SANetworkEngine.HeartEvent e) { e.HeartData = heartdata; }
附录:
现象:服务端打开但是过了一会就关闭了一个连接,或者远程主机关闭了一个连接
原因1:
接收的
监听的队列不够
解决方法:
1.增加监听的队列
2.把监听放到新线程中
原因2:
服务端在接受数据时,客户端突然关闭或者 因为网络原因连接关闭
解决方法:
接收数据应该放在try catch中,使其不影响到下面的传输 ,不应作为一个异常处理
解决方法3:
用阻塞单线程,一个传完了再运行另一个(框架中的方法)
现象:接受完后有些文件或者图片不全
原因:
Arithmetic operation resulted in an overflo scoket发生算术溢出或者算术异常
soket缓冲区只有8k字节,当每次发送的字节太多而网速又不够时 就会发生上面这两种错误
解决方法有两种
1.将发送后的线程等待时间 thread.sleep设得长一点.
2.比较安全的方法:不要一次把整个文件发送,而是分段发送,而且每段的字节不超过8字节 也就是 8x1024 byte (一般设置为2x1024byte或者 4x1024byte)
相关文章推荐
- 小兵传奇三:scoket网络编程传输图片
- 网络编程二:TCP传输、TCP双向传输、文本转换、并发上传图片、客户端并发登录
- .NET(C#)基于Socket编程实现平行主机之间网络通讯有图片传输的Demo演示
- Java 网络编程三 TCP传输协议(例:传输文本、图片)
- Base64编码在网络图片传输中的应用实例
- 关于Delphi的网络传输编程
- 黑马程序员-网络编程 tcp传输 URL
- 网络编程(TCP上传图片文件)
- Linux网络编程之使用TCP传输文件
- 网络编程、三要素、Socket通信、UDP传输、TCP协议、服务端(二十五)
- Sockets编程--网络传输对象(采用异步机制)
- C#网络编程(同步传输字符串) - Part.2
- [javaSE] 网络编程(TCP-并发上传图片)
- iOS UI 15 网络编程下载 图片 音乐 大文件 视频 get/ post方法
- Java基础-网络编程(TCP-上传图片)
- C# 网络编程之webBrowser获取网页url和下载网页中图片
- iOS网络编程解析协议三:JSON数据传输解析
- 网络编程之网络图片浏览
- Linux网络编程之使用UDP传输文件
- C#网络编程(同步传输字符串) - Part.2