C#之Socket操作类实例解析

本文展示了一个C#的Socket操作类的完整实例,并附带了用法说明,分享给大家供大家参考之用。具体方法如下:

主要功能代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Collections;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Collections.Specialized;
using System.Threading;
public class DuxSocketClient
{
 #region 私有字段
 /// <summary>
 /// 设置数据缓冲区大小 默认1024
 /// </summary>
 private static int m_maxpacket = 1024 * 4;
 public delegate void SendFileProgress(int progress);
 public delegate void ReceiveFileProgress(int progress);
 #endregion
 #region 服务器侦听
 /// <summary>
 /// 服务器侦听方法 返回null则说明没有链接上
 /// </summary>
 /// <returns>返回一个套接字(Socket)</returns>
 public static Socket ListenerSocket(TcpListener listener)
 {
  try
  {
   Socket socket = listener.AcceptSocket();
   return socket;
  }
  catch
  {
   return null;
  }
 }
 /// <summary>
 /// 服务器侦听方法 返回null则说明没有链接上
 /// </summary>
 /// <param name="listener"></param>
 /// <returns>返回一个网络流</returns>
 public static NetworkStream ListenerStream(TcpListener listener)
 {
  try
  {
   TcpClient client = listener.AcceptTcpClient();
   return client.GetStream();
  }
  catch
  {
   return null;
  }
 }
 #endregion
 #region 客户端连接
 public static Socket ConnectSocket(TcpClient tcpclient, IPEndPoint ipendpoint)
 {
  try
  {
   tcpclient.Connect(ipendpoint);
   return tcpclient.Client;
  }
  catch
  {
   return null;
  }
 }
 public static Socket ConnectSocket(TcpClient tcpclient, IPAddress ipadd, int port)
 {
  try
  {
   tcpclient.Connect(ipadd, port);
   return tcpclient.Client;
  }
  catch
  {
   return null;
  }
 }
 public static NetworkStream ConnectStream(TcpClient tcpclient, IPEndPoint ipendpoint)
 {
  try
  {
   tcpclient.Connect(ipendpoint);
   return tcpclient.GetStream();
  }
  catch
  {
   return null;
  }
 }
 public static NetworkStream ConnectStream(TcpClient tcpclient, IPAddress ipadd, int port)
 {
  try
  {
   tcpclient.Connect(ipadd, port);
   return tcpclient.GetStream();
  }
  catch
  {
   return null;
  }
 }
 #endregion
 #region Socket接收数据
 /// <summary>
 /// 接受固定长度字符串
 /// </summary>
 /// <param name="socket"></param>
 /// <param name="size"></param>
 /// <returns></returns>
 public static byte[] ReceiveFixData(Socket socket, int size)
 {
  int offset = 0;
  int recv = 0;
  int dataleft = size;
  byte[] msg = new byte[size];
  while (dataleft > 0)
  {
   recv = socket.Receive(msg, offset, dataleft, 0);
   if (recv == 0)
   {
    break;
   }
   offset += recv;
   dataleft -= recv;
  }
  return msg;
 }
 /// <summary>
 /// 接收变长字符串
 /// 为了处理粘包问题 ,每次发送数据时 包头(数据字节长度) + 正文
 /// 这个发送小数据
 /// 设置包头的字节为8,不能超过8位数的字节数组
 /// </summary>
 /// <param name="socket"></param>
 /// <returns>byte[]数组</returns>
 public static byte[] ReceiveVarData(Socket socket)
 {
  //每次接受数据时,接收固定长度的包头,包头长度为8
  byte[] lengthbyte = ReceiveFixData(socket, 8);
  //length得到字符长度 然后加工处理得到数字
  int length = GetPacketLength(lengthbyte);
  //得到正文
  return ReceiveFixData(socket, length);
 }
 /// <summary>
 /// 接收T类对象,反序列化
 /// </summary>
 /// <typeparam name="T">接收T类对象,T类必须是一个可序列化类</typeparam>
 /// <param name="socket"></param>
 /// <returns></returns>
 public static T ReceiveVarData<T>(Socket socket)
 {
  //先接收包头长度 固定8个字节
  byte[] lengthbyte = ReceiveFixData(socket, 8);
  //得到字节长度
  int length = GetPacketLength(lengthbyte);
  byte[] bytecoll = new byte[m_maxpacket];
  IFormatter format = new BinaryFormatter();
  MemoryStream stream = new MemoryStream();
  int offset = 0; //接收字节个数
  int lastdata = length; //还剩下多少没有接收,初始大小等于实际大小
  int receivedata = m_maxpacket; //每次接收大小
  //循环接收
  int mark = 0; //标记几次接收到的数据为0长度
  while (true)
  {
   //剩下的字节数是否小于缓存大小
   if (lastdata < m_maxpacket)
   {
    receivedata = lastdata; //就只接收剩下的字节数
   }
   int count = socket.Receive(bytecoll,0,receivedata,0);
   if (count > 0)
   {
    stream.Write(bytecoll, 0, count);
    offset += count;
    lastdata -= count;
    mark = 0;
   }
   else
   {
    mark++;
    if (mark == 10)
    {
     break;
    }
   }
   if (offset == length)
   {
    break;
   }
  }
  stream.Seek(0, SeekOrigin.Begin); //必须要这个 或者stream.Position = 0;
  T t = (T)format.Deserialize(stream);
  stream.Close();
  return t;
 }
 /// <summary>
 /// 在预先得到文件的文件名和大小
 /// 调用此方法接收文件
 /// </summary>
 /// <param name="socket"></param>
 /// <param name="path">路径必须存在</param>
 public static bool ReceiveFile(Socket socket, string path, string filename, long size,ReceiveFileProgress progress)
 {
  bool ret = false;
  if (Directory.Exists(path))
  {
   //主要是防止有重名文件
   string savepath = GetPath(path, filename); //得到文件路径
   //缓冲区
   byte[] file = new byte[m_maxpacket];
   int count = 0; //每次接收的实际长度
   int receivedata = m_maxpacket; //每次要接收的长度
   long offset = 0; //循环接收的总长度
   long lastdata = size; //剩余多少还没接收
   int mark = 0;
   using (FileStream fs = new FileStream(savepath, FileMode.OpenOrCreate, FileAccess.Write))
   {
    if (size > 0)
    {
     while (true)
     {
      if (lastdata < receivedata)
      {
       receivedata = Convert.ToInt32(lastdata);
      }
      count = socket.Receive(file, 0, receivedata, SocketFlags.None);
      if (count > 0)
      {
       fs.Write(file, 0, count);
       offset += count;
       lastdata -= count;
       mark = 0;
      }
      else
      {
       mark++; //连续5次接收为0字节 则跳出循环
       if (mark ==10)
       {
        break;
       }
      }
      //接收进度
      if (progress != null)
      {
       progress(Convert.ToInt32(((Convert.ToDouble(offset) / Convert.ToDouble(size)) * 100)));
      }
      //接收完毕
      if (offset == size)
      {
       ret = true;
       break;
      }
     }
    }
    fs.Close();
   }
  }
  return ret;
 }
 public static bool ReceiveFile(Socket socket, string path, string filename, long size)
 {
  return ReceiveFile(socket, path, filename, size,null);
 }
 /// <summary>
 /// 预先不知道文件名和文件大小 用此方法接收
 /// 此方法对于的发送方法是SendFile()
 /// </summary>
 /// <param name="socket"></param>
 /// <param name="path">要保存的目录</param>
 public static void ReceiveFile(Socket socket, string path)
 {
  //得到包头信息字节数组 (文件名 + 文件大小 的字符串长度)
  //取前8位
  byte[] info_bt = ReceiveFixData(socket, 8);
  //得到包头信息字符长度
  int info_length = GetPacketLength(info_bt);
  //提取包头信息,(文件名 + 文件大小 的字符串长度)
  byte[] info = ReceiveFixData(socket, info_length);
  //得到文件信息字符串 (文件名 + 文件大小)
  string info_str = System.Text.Encoding.UTF8.GetString(info);
  string[] strs = info_str.Split(‘|‘);
  string filename = strs[0]; //文件名
  long length = Convert.ToInt64(strs[1]); //文件大小
  //开始接收文件
  ReceiveFile(socket, path, filename, length);
 }
 private static int GetPacketLength(byte[] length)
 {
  string str = System.Text.Encoding.UTF8.GetString(length);
  str = str.TrimEnd(‘*‘); ;//("*", "");
  int _length = 0;
  if (int.TryParse(str, out _length))
  {
   return _length;
  }
  else
  {
   return 0;
  }
 }
 /// <summary>
 /// 得到文件路径(防止有文件名重复)
 /// 如:aaa.txt已经在directory目录下存在,则会得到文件aaa(1).txt
 /// </summary>
 /// <param name="directory">目录名</param>
 /// <param name="file">文件名</param>
 /// <returns>文件路径</returns>
 static int i = 0;
 static string markPath = String.Empty;
 public static string GetPath(string directory, string file)
 {
  if (markPath == String.Empty)
  {
   markPath = Path.Combine(directory, file);
  }
  string path = Path.Combine(directory, file);
  if (File.Exists(path))
  {
   i++;
   string filename = Path.GetFileNameWithoutExtension(markPath) + "(" + i.ToString() + ")";
   string extension = Path.GetExtension(markPath);
   return GetPath(directory, filename + extension);
  }
  else
  {
   i = 0;
   markPath = String.Empty;
   return path;
  }
 }
 #endregion
 #region Socket发送数据
 /// <summary>
 /// 发送固定长度消息
 /// 发送字节数不能大于int型最大值
 /// </summary>
 /// <param name="socket"></param>
 /// <param name="msg"></param>
 /// <returns>返回发送字节个数</returns>
 public static int SendFixData(Socket socket, byte[] msg)
 {
  int size = msg.Length; //要发送字节长度
  int offset = 0;     //已经发送长度
  int dataleft = size;  //剩下字符
  int senddata = m_maxpacket; //每次发送大小
  while (true)
  {
   //如过剩下的字节数 小于 每次发送字节数
   if (dataleft < senddata)
   {
    senddata = dataleft;
   }
   int count = socket.Send(msg, offset, senddata, SocketFlags.None);
   offset += count;
   dataleft -= count;
   if (offset == size)
   {
    break;
   }
  }
  return offset;
 }
 /// <summary>
 /// 发送变长信息 格式 包头(包头占8位) + 正文
 /// </summary>
 /// <param name="socket"></param>
 /// <param name="contact">发送文本</param>
 /// <returns></returns>
 public static int SendVarData(Socket socket, string contact)
 {
  //得到字符长度
  int size = System.Text.Encoding.UTF8.GetBytes(contact).Length;
  //包头字符
  string length = GetSendPacketLengthStr(size);
  //包头 + 正文
  byte[] sendbyte = System.Text.Encoding.UTF8.GetBytes(length + contact);
  //发送
  return SendFixData(socket, sendbyte);
 }
 /// <summary>
 /// 发送变成信息
 /// </summary>
 /// <param name="socket"></param>
 /// <param name="bytes"></param>
 /// <returns></returns>
 public static int SendVarData(Socket socket, byte[] bytes)
 {
  //得到包头字节
  int size = bytes.Length;
  string length = GetSendPacketLengthStr(size);
  byte[] lengthbyte = System.Text.Encoding.UTF8.GetBytes(length);
  //发送包头
  SendFixData(socket, lengthbyte);  //因为不知道正文是什么编码所以没有合并
  //发送正文
  return SendFixData(socket, bytes);
 }
 /// <summary>
 /// 发送T类型对象,序列化
 /// </summary>
 /// <typeparam name="T">T类型</typeparam>
 /// <param name="socket"></param>
 /// <param name="obj">T类型对象,必须是可序列化的</param>
 /// <returns></returns>
 public static int SendSerializeObject<T>(Socket socket, T obj)
 {
  byte[] bytes = SerializeObject(obj);
  return SendVarData(socket, bytes);
 }
 /// <summary>
 /// 发送文件
 /// </summary>
 /// <param name="socket">socket对象</param>
 /// <param name="path">文件路径</param>
 /// <param name="issend">是否发送文件(头)信息,如果当前知道文件[大小,名称]则为false</param>
 /// <param name="progress"></param>
 /// <returns></returns>
 public static bool SendFile(Socket socket, string path,bool issend,SendFileProgress progress)
 {
  bool ret = false;
  if (File.Exists(path))
  {
   FileInfo fileinfo = new FileInfo(path);
   string filename = fileinfo.Name;
   long length = fileinfo.Length;
   //发送文件信息
   if (issend)
   {
    SendVarData(socket, filename + "|" + length);
   }
   //发送文件
   long offset = 0;
   byte[] b = new byte[m_maxpacket];
   int mark = 0;
   using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
   {
    int senddata = b.Length;
    long i = length;
    //循环读取发送
    while (true)
    {
     int count = fs.Read(b, 0, senddata);
     if (count > 0)
     {
      socket.Send(b, 0, count, SocketFlags.None);
      offset += count;
      mark = 0;
     }
     else
     {
      mark++;
      if (mark == 10)
      {
       break;
      }
     }
     if (progress != null)
     {
      progress(Convert.ToInt32(((Convert.ToDouble(offset) / Convert.ToDouble(length)) * 100)));
     }
     if (offset == length)
     {
      break;
     }
     Thread.Sleep(50); //设置等待时间,以免粘包
    }
   }
  }
  return ret;
 }
 /// <summary>
 /// 发送文件,不需要进度信息
 /// </summary>
 /// <param name="socket">socket对象</param>
 /// <param name="path">文件路径</param>
 /// <param name="issend">是否发生(头)信息</param>
 /// <returns></returns>
 public static bool SendFile(Socket socket, string path,bool issend)
 {
  return SendFile(socket, path, issend, null);
 }
 /// <summary>
 /// 发送文件,不需要进度信息和(头)信息
 /// </summary>
 /// <param name="socket">socket对象</param>
 /// <param name="path">文件路径</param>
 /// <returns></returns>
 public static bool SendFile(Socket socket, string path)
 {
  return SendFile(socket, path, false, null);
 }
 private static byte[] SerializeObject(object obj)
 {
  IFormatter format = new BinaryFormatter();
  MemoryStream stream = new MemoryStream();
  format.Serialize(stream, obj);
  byte[] ret = stream.ToArray();
  stream.Close();
  return ret;
 }
 private static string GetSendPacketLengthStr(int size)
 {
  string length = size.ToString() + "********"; //得到size的长度
  return length.Substring(0, 8); //截取前前8位
 }
 #endregion
 #region NetworkStream接收数据
 //没写
 #endregion
 #region NetworkStream发送数据
 //没写
 #endregion
}


用法说明:

每个接收的方法都对应着有发送方法

如:

发送方法:

SendFixData(socket,"01");

接收方法:

ReceiveFixData(socket,2); //size 就为2

不知道发送文本长度:

string txt = ????  //不知道有多少字符

发送方法:

SendVarData(socket,txt); //有重载版

接收方法:

ReceiveVarData(socket);

希望本文所述实例对大家C#程序设计有所帮助。

除声明外,跑步客文章均为原创,转载请以链接形式标明本文地址
  C#之Socket操作类实例解析

本文地址:  http://www.paobuke.com/develop/c-develop/pbk23586.html

相关内容

C# 常用公共方法

C#中的 == 和equals()区别浅析

C#访问SQL Server数据库的实现方法

详解TreeView绑定数据库


C#实现的字符串相似度对比类

C#执行外部命令的方法

C#窗体显示留言时间的方法

C#通过IComparable实现ListT.sort()排序

时间: 2024-10-13 18:19:29

C#之Socket操作类实例解析的相关文章

C# Oracle数据库操作类实例详解

本文所述为C#实现的Oracle数据库操作类,可执行超多常用的Oracle数据库操作,包含了基础数据库连接.关闭连接.输出记录集.执行Sql语句,返回带分页功能的dataset .取表里字段的类型和长度等,同时还有哈稀表自动插入数据库等高级任务.需要特别指出的是:在执行SQL语句,返回 DataReader之前一定要先用.read()打开,然后才能读到数据,再用hashTable对数据库进行insert,update,del操作,注意此时只能用默认的数据库连接"connstr". 本文

C#的SQL操作类实例

本文实例讲述了C#的SQL操作类,分享给大家供大家参考.具体方法如下: 代码如下: using System; using System.Collections.Generic; using System.Text; using System.Data; using System.Data.SqlClient; using System.Windows.Forms; namespace yjgl { /// <summary> /// 数据访问基础类(SQL) /// </summary

C#封装的常用文件操作类实例

本文实例讲述了C#封装的常用文件操作类.分享给大家供大家参考.具体如下: 这个C#类封装了我们经常能用到的文件操作方法,包括读写文件.获取文件扩展名.复制文件.追加内容到文件.删除文件.移动文件.创建目录.递归删除文件及目录.列目录.列文件等,不可多得. using System; using System.Text; using System.Web; using System.IO; namespace DotNet.Utilities { public class FileOperate

C#使用iTextSharp封装的PDF文件操作类实例

本文实例讲述了C#使用iTextSharp封装的PDF文件操作类.分享给大家供大家参考.具体分析如下: 这个C#代码主要讲iTextSharp中用于操作PDF文件的方法进行了再次封装,可以更加方便的访问PDF文档,可以动态生成PDF文件.添加内容.设置段落.设置字体等. using System.IO; using iTextSharp.text; using iTextSharp.text.pdf; namespace DotNet.Utilities { /// <summary> ///

C#实现的JS操作类实例

本文实例讲述了C#实现的JS操作类.分享给大家供大家参考.具体如下: 这个C#类封装了常用的JS客户端代码操作,包括弹出对话框.返回上一页,通过JS转向,弹出警告框并转向等. using System.Web; namespace DotNet.Utilities { /// <summary> /// 客户端脚本输出 /// </summary> public class JsHelper { /// <summary> /// 弹出信息,并跳转指定页面. /// &

应用程序框架实战十二:公共操作类开发技巧(初学者必读)

本文专门为初学者而写,因为很多初学者可能还不了解公共操作类的作用和封装技巧,大部分有经验的程序员都会把自己所碰到的技术问题整理封装成类,这就是公共操作类.公共操作类往往具有一些通用性,也可能专门解决某些棘手问题.公共操作类是应用程序框架的核心,主要目标是解决大部分技术问题.我将在本文介绍封装公共操作类的要点,供初学者参考. 开发公共操作类的原因 很多初学者会奇怪,.Net Framework提供的API相当易用,为何还要多此一举,进行一层封装呢.下面列举封装公共操作类的一些动机. .Net Fr

Java常用类库之时间操作类——Date、Calendar、DateFormat、SimpleDateFormat及实例操作

学习目标 掌握Date类的使用 可以使用Calendar类取得一个完整的日期 掌握日期格式化的操作 可以使用SimpleDateFormat进行日期的格式化转换操作 编写取得日期的操作类 进一步掌握Calendar类的使用 进一步掌握SimpleDateFormat类的使用 Date类是一个较为常用的类,但是其操作的日期格式会有一些不符合个人要求,而如果想要进一步取得一些自己需要的时间,则可以使用Calendar类. Date类 在java.util包中定义了Date类,Date类本身使用非常简

C#操作CSV文件类实例

本文实例讲述了C#操作CSV文件类.分享给大家供大家参考.具体分析如下: 这个C#类用于转换DataTable为CSV文件.CSV文件转换成DataTable,如果需要进行CSV和DataTable之间进行转换,使用这个类非常合适. using System.Data; using System.IO; namespace DotNet.Utilities { /// <summary> /// CSV文件转换类 /// </summary> public static class

C#操作session的类实例

本文实例讲述了C#操作session的类.分享给大家供大家参考.具体分析如下: 这个C#类对session操作进行了再次封装,可以大大简化session的常用操作,同时这个类可以将session值设置为数组,也可以将值读取为数组列表,如果你有这方面的需要可以使用这个类,扩这自己对这个C#类进行扩展. using System.Web; namespace DotNet.Utilities { public static class SessionHelper2 { /// <summary>