• 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);

  • 相关阅读:
    ESP32学习目录
    python中mysql管理模块mysql-connector使用
    MYSQL基础知识和操作
    urlib补充
    Python3中Urllib库是什么?urllib模块基本使用
    递归:斐波契那数列
    python正则模块一
    模块&包
    XML模块示例代码
    使用python操作XML增删改查
  • 原文地址:https://www.cnblogs.com/xuexian/p/3532128.html
Copyright © 2020-2023  润新知