【问题标题】:Ideas to manage IP and Ports in Key/Value format?以 Key/Value 格式管理 IP 和端口的想法?
【发布时间】:2017-01-12 00:13:37
【问题描述】:

我正在寻找一种良好且快速的方法来管理文件中的 IP 地址和端口。具有 2 列的 DB 表的排序:IP 和端口,但在文件中,不使用 DB。

它必须支持添加、删除和更新。我不在乎并发。

【问题讨论】:

    标签: c# database file key-value


    【解决方案1】:

    下面,有人来完成你的任务。我试图直截了当,所以可能缺少一些东西。

    我想创建一个“记录”类,以保持 ip/port 对

    class Record : IPEndPoint, IComparable<Record>
    {
        internal long Offset { get; set; }
        public bool Deleted  { get; internal set; }
    
        public Record() : base(0, 0)
        { 
            Offset = -1;
            Deleted = false;
        }
    
        public int CompareTo(Record other)
        {
            if (this.Address == other.Address && this.Address == other.Address )
                return 0;
            else if (this.Address == other.Address)
                return this.Port.CompareTo(other.Port);
            else
                return 
                  BitConverter.ToInt32(this.Address.GetAddressBytes(), 0).CompareTo(
                  BitConverter.ToInt32(other.Address.GetAddressBytes(), 0));
        }
    }
    
    class RecordComparer : IComparer<Record>
    {
        public int Compare(Record x, Record y)
        {
            return x.CompareTo(y);
        }
    }
    

    ...还有一个“DatabaseFile”类来管理数据文件交互。

    class DatabaseFile : IDisposable
    {
        private FileStream file;
        private static int RecordSize = 7;
        private static byte[] Deleted = new byte[] { 42 };
        private static byte[] Undeleted = new byte[] { 32 };
        public DatabaseFile(string filename)
        {
            file = new FileStream(filename, 
                FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
        }
    
        public IEnumerable<Record> Locate(Predicate<Record> record)
        {
            file.Seek(0, SeekOrigin.Begin);
            while (file.Position < file.Length)
            {
                long offset = file.Position;
                byte[] buffer = new byte[DatabaseFile.RecordSize];
                file.Read(buffer, 0, DatabaseFile.RecordSize);
                Record current = Build(offset, buffer);
                if (record.Invoke(current))
                    yield return current;
            }
        }
    
        public void Append(Record record)
        {
            // should I look for duplicated values? i dunno
            file.Seek(0, SeekOrigin.End);
            record.Deleted = false;
            record.Offset = file.Position;
            Write(record);
        }
    
        public void Delete(Record record)
        {
            if (record.Offset == -1) return;
            file.Seek(record.Offset, SeekOrigin.Begin);
            record.Deleted = true;
            Write(record);
        }
    
        public void Update(Record record)
        {
            if (record.Offset == -1)
            {
                Append(record);
            }
            else
            {
                file.Seek(record.Offset, SeekOrigin.Begin);
                Write(record);
            }
        }
    
        private void Write(Record record)
        {
            file.Write(GetBytes(record), 0, DatabaseFile.RecordSize);
        }
    
        private Record Build(long offset, byte[] data)
        {
            byte[] ipAddress = new byte[4];
            Array.Copy(data, 1, ipAddress, 0, ipAddress.Length);
            return new Record
            {
                Offset = offset,
                Deleted = (data[0] == DatabaseFile.Deleted[0]),
                Address = new IPAddress(ipAddress), 
                Port = BitConverter.ToInt16(data, 5)
            };
        }
    
        private byte[] GetBytes(Record record)
        {
            byte[] returnValue = new byte[DatabaseFile.RecordSize];
            Array.Copy(
                record.Deleted ? DatabaseFile.Deleted : DatabaseFile.Undeleted, 0, 
                returnValue, 0, 1);
            Array.Copy(record.Address.GetAddressBytes(), 0, 
                returnValue, 1, 4);
            Array.Copy(BitConverter.GetBytes(record.Port), 0, 
                returnValue, 5, 2);
            return returnValue;
        }
    
        public void Pack()
        {
            long freeBytes = 0;
            byte[] buffer = new byte[RecordSize];
            Queue<long> deletes = new Queue<long>();
    
            file.Seek(0, SeekOrigin.Begin);
            while (file.Position < file.Length)
            {
                long offset = file.Position;
                file.Read(buffer, 0, RecordSize);
                if (buffer[0] == Deleted[0])
                {
                    deletes.Enqueue(offset);
                    freeBytes += RecordSize;
                }
                else
                {
                    if (deletes.Count > 0)
                    {
                        deletes.Enqueue(offset);
                        file.Seek(deletes.Dequeue(), SeekOrigin.Begin);
                        file.Write(buffer, 0, RecordSize);
                        file.Seek(offset + RecordSize, SeekOrigin.Begin);
                    }
                }
            }
            file.SetLength(file.Length - freeBytes);
        }
    
        public void Sort()
        {
            int offset = -RecordSize; // lazy method
            List<Record> records = this.Locate(r => true).ToList();
            records.Sort(new RecordComparer());
            foreach (Record record in records)
            {
                record.Offset = offset += RecordSize;
                Update(record);
            }
        }
    
        public void Dispose()
        {
            if (file != null)
                file.Close();
        }
    }
    

    下面是一个工作示例:

    static void Main(string[] args)
    {
        List<IPEndPoint> endPoints = new List<IPEndPoint>(
            new IPEndPoint[]{
                new IPEndPoint(IPAddress.Parse("127.0.0.1"), 80),
                new IPEndPoint(IPAddress.Parse("69.59.196.211"), 80),
                new IPEndPoint(IPAddress.Parse("74.125.45.100"), 80)
            });
        using (DatabaseFile dbf = new DatabaseFile("iptable.txt"))
        {
            foreach (IPEndPoint endPoint in endPoints)
                dbf.Append(new Record { 
                    Address = endPoint.Address, 
                    Port = endPoint.Port });
    
            Record stackOverflow = dbf.Locate(r => 
                Dns.GetHostEntry(r.Address)
                    .HostName.Equals("stackoverflow.com")).FirstOrDefault();
            if (stackOverflow != null)
                dbf.Delete(stackOverflow);
    
            Record google = dbf.Locate(r =>
                r.Address.ToString() == "74.125.45.100").First();
            google.Port = 443;
            dbf.Update(google);
    
            foreach(Record http in dbf.Locate(r => 
                !r.Deleted && r.Port == 80))
                Console.WriteLine(http.ToString());
        }
        Console.ReadLine();
    }
    

    dBase III,我想念你。

    嗯,这很有趣,谢谢!

    编辑 1:添加了 Pack() 和懒惰的 Sort() 代码;

    编辑 2:添加了缺少的 IComparable/IComparer 实现

    【讨论】:

    • 很高兴听到这个消息,阿隆,泰;出于好奇:您打算在哪里使用它?
    【解决方案2】:

    我个人会去

    192.100.10.1:500:20-21

    192.100.10.2:27015-27016:80

    第一个是 IP,: 之后的所有东西都是一个端口,我们也可以用- 表示一个范围,如果我们想对此非常疯狂,我们可以引入一个u,它将表示端口类型UDPTCP 例如:

    192.100.10.2:27015-27016:80:90u

    explode() 很容易适用于上述情况。

    说到插入删除和更新..我们可以简单的创建一个类结构比如

    struct port{
       int portnum;
       char type;
    
       port(int portnum = 0, char type = 't'){
           this.portnum = portnum; this.type = type;
       }
    }
    
    class Ip{
    
        public:
        string Ip_str;
        list <port> prt;
    }
    

    然后你可以让主看起来像

    int main(){
    
        list<Ip> Ips;
    
        //Read the list from file and update the list.
    
        //Sort delete update the list
    
        //Rewrite the list back into file in the order mentioned obove
    
        return 0;
    }
    

    【讨论】:

    • +1:用于范围/协议的想法;我也同意 Robert Harvey 关于小内存使用的观点,但 Alon 说要避免使用内存中的地址。
    【解决方案3】:

    最简单的方法可能是创建一个包含您的 IP 和端口的小类

    class IpAddress
    {
        public string IP;
        public int port;
    }
    

    然后创建一个list&lt;IpAddress&gt;。然后,您可以使用 XML 序列化和反序列化来读取和写入列表中的文件。

    【讨论】:

    • 在您的示例中,我需要先将整个数据加载到内存中,然后在内存中插入/更新/删除,然后重写整个文件。我需要一个不需要将整个数据加载到内存或重写整个文件的解决方案,我需要一个只进行特定插入/更新/删除操作的解决方案。
    • 我们在谈论多少个 IP?即使是数千个,文件仍然会相对较小。
    • 如果你真的想在文件中间添加和删除,可以考虑 SQLite。它是一个轻量级的单文件数据库,运行它的 DLL 非常小。一个 C# 包装器可用。
    【解决方案4】:

    .NET BCL 不提供您正在查找的内容,因为您想在不先将文件加载到内存的情况下查询文件并支持添加/删除。因此,您要么必须推出自己的嵌入式数据库,要么可以简单地使用 SQLite http://www.sqlite.org/

    【讨论】:

      【解决方案5】:

      IP 和端口是一对多的关系。我会考虑这样的事情

      \t192.168.1.1\r\n25\r\n26\r\n\t192.168.1.2\r\n2\r\n80\r\n110

      其中 \t 是制表符,\r\n 是回车后跟换行符

      因此,当您解析时,如果您点击了一个制表符,您就会知道从那里到换行符的那一行中的所有内容都是一个 IP 地址,然后下一个换行符之间的所有内容都是该 IP 地址的端口号,直到您点击一个选项卡,在这种情况下,您使用的是新 IP 地址。这既简单又快速,但不像人类可读的那样。

      【讨论】:

      • 在端口行上做标签不是更易读吗?
      • 是的,可能是这样。好电话。
      【解决方案6】:

      这与 IP 和端口无关。问题是,据我所知,Windows 不允许在文件中间插入或删除字节。

      【讨论】:

        猜你喜欢
        • 2018-05-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-12
        • 2013-03-23
        • 2022-12-03
        • 1970-01-01
        • 2020-02-04
        相关资源
        最近更新 更多