• [知识整理]使用RawSocket 抓包


        曾经写过一个远程网络抓包工具,为啥原远程抓了?在解决现网问题,或者统计数据,需要快速准确的抓取特点的网络吧。我们应用都是windows机器,经常需要抓XX服务某A机到XX服务B机的网络包,定位机器、查部署情况、IP地址、负载配置、同时操作多台设备等等都特别耗时,因为有了这个工具的设计初衷。于是开始设计使用调用Winpcap组件实现去抓,后来发现生产环境很多机器并未安装,同时winpcap 也无法支持silence方式安装。

       所以不得不想其他方案,这里就想到了RawSocket了,通过RawSocket 获取全量网络包,让后实现winpcap相同的filter 功能,将RawSocket 数据量保持为标准的pcap格式文件,问题得到完美的解决。

    代码实现如下:

    1. rawsocket

       1:    public class RawSocket
       2:      {
       3:          private Socket _socket;
       4:          private IPAddress _address;
       5:          public Action<TcpPacket> OnTcpPacketCapture;
       6:          public Action<byte[], int> OnRawDataCapure;
       7:          public RawSocket(IPAddress address)
       8:          {
       9:              _address = address;
      10:              _socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
      11:              _socket.Bind(new IPEndPoint(address, 0));
      12:          }
      13:   
      14:          public bool Capture()
      15:          {
      16:              bool isOk = true;
      17:              try
      18:              {
      19:                  _socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1);
      20:                  byte[] inBytes = new byte[] { 1, 0, 0, 0 };
      21:                  byte[] outBytes = new byte[] { 0, 0, 0, 0 };
      22:                  _socket.IOControl(IOControlCode.ReceiveAll, inBytes, outBytes);
      23:                  if (0 != outBytes[0] + outBytes[1] + outBytes[2] + outBytes[3]) { isOk = false; }
      24:              }
      25:              catch (SocketException)
      26:              {
      27:                  isOk = false;
      28:              }
      29:   
      30:              if (isOk)
      31:              {
      32:                  while (true)
      33:                  {
      34:                      //以太网MTU 最大为1500
      35:                      byte[] buffer = new byte[1500];
      36:   
      37:                      int count = _socket.Receive(buffer, SocketFlags.None);
      38:   
      39:                      if (OnRawDataCapure != null)
      40:                          OnRawDataCapure(buffer, count);
      41:   
      42:                      if (OnTcpPacketCapture != null)
      43:                      {
      44:                          IPPacket ip = new IPPacket(buffer,0,count);
      45:                          Console.WriteLine(ip);
      46:                          if (ip.Protocol == IPProtocolType.TCP)
      47:                          {
      48:                              TcpPacket tcp = new TcpPacket(ip);
      49:                              OnTcpPacketCapture(tcp);
      50:                          }
      51:                      }
      52:                  }
      53:              }
      54:   
      55:              return isOk;
      56:          }
      57:   
      58:          public void Stop()
      59:          {
      60:              if (_socket != null)
      61:              {
      62:                  _socket.Shutdown(SocketShutdown.Both);
      63:                  _socket.Close();
      64:              }
      65:          }
      66:      }
      89:      class SocketData
      90:      {
      91:          public Socket Socket { get; set; }
      92:          public Byte[] Data { get; set; }
      93:      }
      94:      public class DataBuffer
      95:      {
      96:          public byte[] RawBuffer;
      97:          public int RawStart;
      98:          public int RawCount;
      99:   
     100:          private byte[] buffer;
     101:          private int start;
     102:          private int end;
     103:          public int Length { get { return end - start; } }
     104:          public byte this[int index] { get { return buffer[start + index]; } }
     105:   
     106:          public DataBuffer(byte[] data)
     107:              : this(data, 0, data.Length) { }
     108:   
     109:          public DataBuffer(byte[] data, int start, int count)
     110:          {
     111:             this.RawBuffer= this.buffer = data;
     112:             this.RawStart= this.start = start;
     113:             this.RawCount = count;
     114:              this.end = start + count;
     115:          }
     116:   
     117:          public void SetPosition(int position)
     118:          {
     119:              this.start += position;
     120:          }
     121:   
     122:          public byte[] Data
     123:          {
     124:              get
     125:              {
     126:                  byte[] data = new byte[this.Length];
     127:                  Array.Copy(this.buffer, this.start, data, 0, data.Length);
     128:                  return data;
     129:              }
     130:          }
     131:      }
     132:   
     133:      public class IPPacket
     134:      {
     135:          public int Version; //版本号
     136:          public int HeadLen; //头长度
     137:          public int DiffServices; //区分服务
     138:          public int DataLen;//数据长度
     139:          public int Identification; //标志
     140:          public int Flag; //标识 3bit
     141:          public int Excursion;//片偏移 13bit
     142:          public byte TTL;//生存周期
     143:          public IPProtocolType Protocol; //协议
     144:          public int CheckSum; //校验和
     145:          public IPAddress SrcAddr; //源地址
     146:          public IPAddress DestAddr; //目标地址
     147:          public byte[] option; //选项
     148:          public DataBuffer Data; //IP Payload
     149:   
     150:          public IPPacket(byte[] data) : this(new DataBuffer(data)) { }
     151:          public IPPacket(byte[] data, int start, int count) : this(new DataBuffer(data, start, count)) { }
     152:   
     153:          public IPPacket(DataBuffer data)
     154:          {
     155:              Version = (data[0] & 0xF0) >> 4;
     156:              HeadLen = (int)(data[0] & 0x0F) * 4;
     157:              DiffServices = (int)data[1];
     158:              DataLen = ((int)data[2] << 8) + (int)data[3];
     159:              Identification = ((int)data[5] << 8) + (int)data[5];
     160:              Flag = data[6] >> 5;
     161:              Excursion = (((int)data[6] & 0x1F) << 8) + (int)data[7];
     162:              TTL = data[8];
     163:              Protocol = (IPProtocolType)data[9];
     164:              CheckSum = ((int)data[10] << 8) + (int)data[11];
     165:   
     166:              byte[] addr = new byte[4];
     167:              for (int i = 0; i < 4; i++)
     168:                  addr[i] = data[12 + i];
     169:              SrcAddr = new IPAddress(addr);
     170:   
     171:              addr = new byte[4];
     172:              for (int i = 0; i < 4; i++)
     173:                  addr[i] = data[16 + i];
     174:              DestAddr = new IPAddress(addr);
     175:   
     176:              //option
     177:              if (HeadLen > 20)
     178:              {
     179:                  option = new byte[HeadLen - 20];
     180:                  for (int i = 0; i < option.Length; i++)
     181:                      option[i] = data[20 + i]; //会包括填充部分
     182:              }
     183:   
     184:              data.SetPosition(HeadLen);
     185:              Data = data;
     186:          }
     187:   
     188:          public override string ToString()
     189:          {
     190:              StringBuilder sb = new StringBuilder();
     191:              sb.AppendFormat("SrcAddr:{0} DestAddr={1}\r\n", SrcAddr.ToString(), DestAddr.ToString());
     192:              sb.AppendFormat("CheckSum: {0} Protocol:{1}\r\n", CheckSum, Protocol.ToString());
     193:              sb.AppendFormat("Version: {0} HeadLen:{1}\r\n", Version, HeadLen);
     194:              sb.AppendFormat("DataLen: {0} DiffServices:{1}\r\n", DataLen, DiffServices);
     195:              return sb.ToString();
     196:          }
     197:      }
     198:   
     199:      public class TcpPacket
     200:      {
     201:          public int SrcPort = 0;//源端口
     202:          public int DestPort = 0;//目标端口
     203:          public uint SequenceNo = 0;//序号
     204:          public uint NextSeqNo = 0;//确认号
     205:          public int HeadLen = 0;//头长度
     206:          public int Flag = 0;//控制位
     207:          public int WindowSize = 0;//窗口
     208:          public int CheckSum = 0;//校验和
     209:          public int UrgPtr = 0;//紧急指针
     210:          public byte[] option;//选项
     211:          public IPPacket IPPacket;
     212:          public DataBuffer Data;
     213:          public byte[] Body { get { return Data.Data; } }
     214:   
     215:          public TcpPacket(byte[] data) : this(new IPPacket(new DataBuffer(data))) { }
     216:          public TcpPacket(byte[] data, int start, int count) : this(new IPPacket(new DataBuffer(data, start, count))) { }
     217:   
     218:          public TcpPacket(IPPacket packet)
     219:          {
     220:              if (packet.Protocol != IPProtocolType.TCP)
     221:                  throw new NotSupportedException(string.Format("TcpPacket not support {0}", packet.Protocol));
     222:              this.IPPacket = packet;
     223:   
     224:              DataBuffer data = packet.Data;
     225:              SrcPort = ((int)data[0] << 8) + (int)data[1];
     226:              DestPort = ((int)data[2] << 8) + (int)data[3];
     227:   
     228:              SequenceNo = ((uint)data[7] << 24) + ((uint)data[6] << 16) + ((uint)data[5] << 8) + ((uint)data[4]);
     229:              NextSeqNo = ((uint)data[11] << 24) + ((uint)data[10] << 16) + ((uint)data[9] << 8) + ((uint)data[8]);
     230:   
     231:              HeadLen = ((data[12] & 0xF0) >> 4)*4;
     232:              //6bit保留位
     233:              Flag = (data[13] & 0x3F);
     234:              WindowSize = ((int)data[14] << 8) + (int)data[15];
     235:              CheckSum = ((int)data[16] << 8) + (int)data[17];
     236:              UrgPtr = ((int)data[18] << 8) + (int)data[19];
     237:              //option
     238:              if (HeadLen > 20)
     239:              {
     240:                  option = new byte[HeadLen - 20];
     241:                  for (int i = 0; i < option.Length; i++)
     242:                      option[i] = data[20 + i]; //会包括填充部分
     243:              }
     244:              
     245:              data.SetPosition(this.HeadLen);
     246:              Data = data;
     247:          }
     248:   
     249:          public override string ToString()
     250:          {
     251:              StringBuilder sb = new StringBuilder();
     252:              sb.AppendFormat("SrcPort:{0} DestPort={1}\r\n", SrcPort, DestPort);
     253:              sb.AppendFormat("SequenceNo:{0} NextSeqNo={1}\r\n", SequenceNo, NextSeqNo);
     254:              sb.AppendFormat("HeadLen:{0} Flag={1}\r\n", HeadLen, Flag);
     255:              sb.AppendFormat("WindowSize:{0} CheckSum={1}\r\n", WindowSize, CheckSum);
     256:              return sb.ToString();
     257:          }
     258:      }
     259:   
     260:      public enum IPProtocolType : byte
     261:      {
     262:          /// <summary> Dummy protocol for TCP. </summary>
     263:          IP = 0,
     264:          /// <summary> IPv6 Hop-by-Hop options. </summary>
     265:          HOPOPTS = 0,
     266:          /// <summary> Internet Control Message Protocol. </summary>
     267:          ICMP = 1,
     268:          /// <summary> Internet Group Management Protocol.</summary>
     269:          IGMP = 2,
     270:          /// <summary> IPIP tunnels (older KA9Q tunnels use 94). </summary>
     271:          IPIP = 4,
     272:          /// <summary> Transmission Control Protocol. </summary>
     273:          TCP = 6,
     274:          /// <summary> Exterior Gateway Protocol. </summary>
     275:          EGP = 8,
     276:          /// <summary> PUP protocol. </summary>
     277:          PUP = 12,
     278:          /// <summary> User Datagram Protocol. </summary>
     279:          UDP = 17,
     280:          /// <summary> XNS IDP protocol. </summary>
     281:          IDP = 22,
     282:          /// <summary> SO Transport Protocol Class 4. </summary>
     283:          TP = 29,
     284:          /// <summary> IPv6 header. </summary>
     285:          IPV6 = 41,
     286:          /// <summary> IPv6 routing header. </summary>
     287:          ROUTING = 43,
     288:          /// <summary> IPv6 fragmentation header. </summary>
     289:          FRAGMENT = 44,
     290:          /// <summary> Reservation Protocol. </summary>
     291:          RSVP = 46,
     292:          /// <summary> General Routing Encapsulation. </summary>
     293:          GRE = 47,
     294:          /// <summary> encapsulating security payload. </summary>
     295:          ESP = 50,
     296:          /// <summary> authentication header. </summary>
     297:          AH = 51,
     298:          /// <summary> ICMPv6. </summary>
     299:          ICMPV6 = 58,
     300:          /// <summary> IPv6 no next header. </summary>
     301:          NONE = 59,
     302:          /// <summary> IPv6 destination options. </summary>
     303:          DSTOPTS = 60,
     304:          /// <summary> Multicast Transport Protocol. </summary>
     305:          MTP = 92,
     306:          /// <summary> Encapsulation Header. </summary>
     307:          ENCAP = 98,
     308:          /// <summary> Protocol Independent Multicast. </summary>
     309:          PIM = 103,
     310:          /// <summary> Compression Header Protocol. </summary>
     311:          COMP = 108,
     312:          /// <summary> Raw IP packets. </summary>
     313:          RAW = 255,
     314:          /// <summary> IP protocol mask.</summary>
     315:          MASK = 0xff
     316:      }
     

    2. PcapDumper 用户保存pcap文件格式:

       1:   public class PcapDumper
       2:      {
       3:          private PcapStream context;
       4:          public PcapDumper(Stream stream)
       5:          {
       6:              context = new PcapStream(stream);
       7:              PcapHeader header = new PcapHeader();
       8:              header.Writer(context);
       9:              stream.Flush();
      10:          }
      11:   
      12:          public void Write(byte[] buffer, int offset, int count)
      13:          {
      14:              RecordPacket record = new RecordPacket(buffer, offset, count);
      15:              record.Write(context);
      16:          }
      17:   
      18:          public void Write(TcpPacket packet)
      19:          {
      20:              Write(packet.IPPacket.Data.RawBuffer, packet.IPPacket.Data.RawStart, packet.IPPacket.Data.RawCount);
      21:          }
      22:   
      23:          public void Write(IPPacket packet)
      24:          {
      25:              Write(packet.Data.RawBuffer, packet.Data.RawStart, packet.Data.RawCount);
      26:          }
      27:   
      28:          public void Flush()
      29:          {
      30:              context.Flush();
      31:          }
      32:      }
      33:   
      34:      public class PcapHeader
      35:      {
      36:          //for intel x86 save as litle-endian 
      37:          public uint MagicNumber = 0xa1b2c3d4;// native byte ordering. magic number
      38:          public ushort VersionMajor = 0x2; //current 2.4
      39:          public ushort VersionMinor = 0x4;
      40:          public uint TimeZone = 0;//timeezone 实际上没有被使用过
      41:          public uint SigFigs = 0;//useless
      42:          public uint Snaplen = 65535;//maxlen just set the max
      43:          public uint Network = 1;//Ethernet
      44:   
      45:          public void Writer(PcapStream stream)
      46:          {
      47:              stream.Write(MagicNumber);
      48:              stream.Write(VersionMajor);
      49:              stream.Write(VersionMinor);
      50:              stream.Write(TimeZone);
      51:              stream.Write(SigFigs);
      52:              stream.Write(Snaplen);
      53:              stream.Write(Network);
      54:          }
      55:      }
      56:   
      57:   
      58:      public class RecordPacket
      59:      {
      60:          private DateTime unixTime = new DateTime(1970, 1, 1);
      61:          private int EntherNetLen = 14;//srcIP+dstIP+ verion 14b 头
      62:          private int offset;
      63:          private int count;
      64:          private byte[] buffer;
      65:   
      66:          public RecordPacket(byte[] buffer, int offset, int count)
      67:          {
      68:              DateTime time = DateTime.UtcNow;
      69:              TimeSec = (uint)((time - unixTime).TotalSeconds);
      70:              TimeMicroSec = (uint)time.Millisecond * 1000;
      71:              CapLen = Len = (uint)count + 14;
      72:              this.offset = offset;
      73:              this.count = count;
      74:              this.buffer = buffer;
      75:          }
      76:   
      77:          public uint TimeSec;         /* timestamp seconds */
      78:          public uint TimeMicroSec;        /* timestamp microseconds */
      79:          public uint CapLen;       /* number of octets of packet saved in file */
      80:          public uint Len;       /* actual length of packet */
      81:   
      82:          public void Write(PcapStream stream)
      83:          {
      84:              stream.Write(TimeSec);
      85:              stream.Write(TimeMicroSec);
      86:              stream.Write(CapLen);
      87:              stream.Write(Len);
      88:              stream.Write((ulong)1, 6);
      89:              stream.Write((ulong)2, 6); //这两个应该是链路mac地址,随便代替就行
      90:              stream.Write(8);
      91:              stream.Write(0);//IP 0x08
      92:              stream.Write(buffer, offset, count);
      93:          }
      94:      }
      95:   
      96:      public class PcapStream
      97:      {
      98:          private byte[] tempBuffer = new byte[8];
      99:          private Stream stream;
     100:          public PcapStream(Stream stream)
     101:          {
     102:              this.stream = stream;
     103:          }
     104:   
     105:          public void Flush()
     106:          {
     107:              stream.Flush();
     108:          }
     109:   
     110:          public void Write(ushort v)
     111:          {
     112:              Write(v, 2);
     113:          }
     114:   
     115:          public void Write(uint v)
     116:          {
     117:              Write(v, 4);
     118:          }
     119:   
     120:          public void Write(ulong v)
     121:          {
     122:              Write(v, 8);
     123:          }
     124:   
     125:          public void Write(ulong v, int numbytes)
     126:          {
     127:              ulong val = v;
     128:              for (int x = 0; x < numbytes; x++)
     129:              {
     130:                  tempBuffer[x] = (byte)(val & 0xff);
     131:                  val >>= 8;
     132:              }
     133:              stream.Write(tempBuffer, 0, numbytes);
     134:          }
     135:   
     136:          public void Write(byte value)
     137:          {
     138:              stream.WriteByte(value);
     139:          }
     140:   
     141:          public void Write(byte[] buffer, int offset, int count)
     142:          {
     143:              stream.Write(buffer, offset, count);
     144:          }
     145:   
     146:          public void Write(byte[] buffer)
     147:          {
     148:              stream.Write(buffer, 0, buffer.Length);
     149:          }
     150:      }
     151:  }
     152:   
     153:  /// 参考资料:http://wiki.wireshark.org/Development/LibpcapFileFormat
     154:  //magic_number: used to detect the file format itself and the byte ordering. The writing application writes 0xa1b2c3d4 with it's native byte ordering format into this field. The reading application will read either 0xa1b2c3d4 (identical) or 0xd4c3b2a1 (swapped). If the reading application reads the swapped 0xd4c3b2a1 value, it knows that all the following fields will have to be swapped too.
     155:  //version_major, version_minor: the version number of this file format (current version is 2.4)
     156:  //thiszone: the correction time in seconds between GMT (UTC) and the local timezone of the following packet header timestamps. Examples: If the timestamps are in GMT (UTC), thiszone is simply 0. If the timestamps are in Central European time (Amsterdam, Berlin, ...) which is GMT + 1:00, thiszone must be -3600. In practice, time stamps are always in GMT, so thiszone is always 0.
     157:  //sigfigs: in theory, the accuracy of time stamps in the capture; in practice, all tools set it to 0
     158:  //snaplen: the "snapshot length" for the capture (typically 65535 or even more, but might be limited by the user), see: incl_len vs. orig_len below
     159:  //network: link-layer header type, specifying the type of headers at the beginning of the packet (e.g. 1 for Ethernet, see tcpdump.org's link-layer header types page for details); this can be various types such as 802.11, 802.11 with various radio information, PPP, Token Ring, FDDI, etc.
     160:  //0            BSD       loopback devices, except for later OpenBSD
     161:  //1            Ethernet, and Linux loopback devices 
     162:  //6            802.5 Token Ring
     163:  //7            ARCnet
     164:  //8            SLIP
     165:  //9            PPP
     166:  //10          FDDI
     167:  //100        LLC/SNAP-encapsulated ATM
     168:  //101        raw IP, with no link
     169:  //102        BSD/OS SLIP
     170:  //103        BSD/OS PPP
     171:  //104        Cisco HDLC
     172:  //105        802.11
     173:  //108        later OpenBSD loopback devices (with the AF_value in network byte order)
     174:  //113               special Linux cooked capture
     175:  //114               LocalTalk
     176:   
     177:  //ts_sec: the date and time when this packet was captured. This value is in seconds since January 1, 1970 00:00:00 GMT; this is also known as a UN*X time_t. You can use the ANSI C time() function from time.h to get this value, but you might use a more optimized way to get this timestamp value. If this timestamp isn't based on GMT (UTC), use thiszone from the global header for adjustments.
     178:  //ts_usec: the microseconds when this packet was captured, as an offset to ts_sec.  Beware: this value shouldn't reach 1 second (1 000 000), in this case ts_sec must be increased instead!
     179:  //incl_len: the number of bytes of packet data actually captured and saved in the file. This value should never become larger than orig_len or the snaplen value of the global header.
     180:  //orig_len: the length of the packet as it appeared on the network when it was captured. If incl_len and orig_len differ, the actually saved packet size was limited by snaplen.

    3. 最后是Filter功能:

       1:   class PcapFilter
       2:      {
       3:          private const string CONST_AND = "and";
       4:          private const string CONST_OR = "or";
       5:          private const string CONST_HOST = "host";
       6:          private const string CONST_TCP = "tcp";
       7:          private const string CONST_PORT = "port";
       8:   
       9:          private string filter;
      10:          private Func<TcpPacket, bool> filterAction = (packet) => { return true; };
      11:          private List<Func<TcpPacket, bool>> _funcs=new List<Func<TcpPacket,bool>>();
      12:          private bool _isAnd = false;
      13:   
      14:          public PcapFilter(string filter)
      15:          {
      16:              if (!string.IsNullOrEmpty(filter))
      17:              {
      18:                  this.filter = filter.Trim().ToLower();
      19:                  if (!string.IsNullOrEmpty(this.filter))
      20:                      TryParserFilter();
      21:              }
      22:          }
      23:   
      24:          private void TryParserFilter()
      25:          {
      26:              string[] sp = filter.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
      27:              sp = sp.Where(p => p.Trim().Length > 0).ToArray();
      28:   
      29:              if (sp.Where(p => p == CONST_OR || p == CONST_AND).Count() > 1)
      30:                  throw new NotSupportedException("only support one or/and");
      31:   
      32:              TokenState ns = TokenState.Name;
      33:              string name = string.Empty;
      34:   
      35:              for (int i=0;i<sp.Length;i++)
      36:              {
      37:                  if (ns == TokenState.Name)
      38:                  {
      39:                      if (sp[i] == CONST_TCP && sp[i + 1] == CONST_PORT)
      40:                      {
      41:                          name = CONST_TCP;
      42:                          i++;
      43:                          ns = TokenState.Value;
      44:                      }
      45:                      else if (sp[i] == CONST_HOST)
      46:                      {
      47:                          name = CONST_HOST;
      48:                          ns = TokenState.Value;
      49:                      }
      50:                      else
      51:                          throw new NotSupportedException();
      52:                  }
      53:                  else if (ns == TokenState.Operation)
      54:                  {
      55:                      if (sp[i] == CONST_AND)
      56:                          _isAnd = true;
      57:                      else
      58:                          _isAnd = false;
      59:                      ns = TokenState.Name;
      60:                  }
      61:                  else if (ns == TokenState.Value)
      62:                  {
      63:                      ParseAction(name, sp[i]);
      64:                      ns = TokenState.Operation;
      65:                  }
      66:              }
      67:   
      68:              filterAction=(packet)=>
      69:                  {
      70:                      if (_funcs.Count > 0)
      71:                      {
      72:                          if (_funcs.Count == 1)
      73:                              return _funcs[0](packet);
      74:                          else
      75:                          { 
      76:                          if(_isAnd)
      77:                              return _funcs[0](packet) && _funcs[1](packet);
      78:                          }
      79:                      }
      80:                      return true;
      81:                  };
      82:          }
      83:   
      84:          private void ParseAction(string name, string value)
      85:          {
      86:              if (name == CONST_HOST)
      87:              {
      88:                  IPAddress ip = IPAddress.Parse(value);
      89:                  _funcs.Add(new Func<TcpPacket, bool>((pachet) =>
      90:                      {
      91:                          return pachet.IPPacket.SrcAddr.Equals(ip) || pachet.IPPacket.DestAddr.Equals(ip);
      92:                      }));
      93:              }
      94:              else
      95:              {
      96:                  int port = Convert.ToInt32(value);
      97:                  _funcs.Add(new Func<TcpPacket, bool>((packet) =>
      98:                      {
      99:                          return packet.SrcPort == port || packet.DestPort == port;
     100:                      }));
     101:              }
     102:          }
     103:   
     104:          public bool IsFilter(TcpPacket packet)
     105:          {
     106:              return filterAction(packet);
     107:          }
     108:      }
     109:   
     110:      enum TokenState
     111:      { 
     112:          Name,
     113:          Value,
     114:          Operation,
     115:      }
  • 相关阅读:
    20220428 08:00:02
    20220427 08:00:02
    Belong to you
    今天看到疫情驰援的人发布的视频,感触很多
    用生活治愈生活
    站 又名《standard》
    在mysql中,如果传入的startTime为20201112,20201113这样的字符串,则可以使用find_in_set取代in
    界面控件DevExpress WinForms MVVM入门指南——登录表单(下)
    界面控件DevExpress WPF v21.2 可访问性功能升级
    UI组件Kendo UI for jQuery数据管理教程 TaskBoard/搜索工具辑
  • 原文地址:https://www.cnblogs.com/lulu/p/3130830.html
Copyright © 2020-2023  润新知