Pergunta

Estou procurando um bom e velozes Maneira de gerenciar endereços IP e portas em um arquivo. Uma espécie de tabela DB que possui 2 colunas: IP e porta, mas em um arquivo, sem usar um db.

Ele precisa suportar adicionar, excluir e atualizar. Eu não me importo com a simultaneidade.

Foi útil?

Solução

Abaixo, alguns vêm para concluir sua tarefa. Eu tentei ir estritamente direto ao ponto, então talvez algo esteja faltando.

Eu criaria uma classe de "registro", para manter os pares de IP/portas

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

... e uma classe "DatabaseFile" para gerenciar a interação do arquivo de dados.

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();
    }
}

Abaixo, um exemplo de funcionamento:

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, sinto sua falta.

Bem, isso foi divertido, obrigado!

Editar 1: Adicionado Pack() e preguiçoso Sort() código;

Editar 2: Adicionado faltando IComparable/IComparer implementação

Outras dicas

Eu pessoalmente vou para

192.100.10.1:500:20-21

192.100.10.2:27015-27016:80

Onde o primeiro é o IP e tudo depois do : é um porto, também podemos representar um intervalo por - E se queremos ser muito loucos por isso, podemos apresentar um u que representará o tipo de porta UDP ou TCP por exemplo:

192.100.10.2:27015-27016:80:90u

E explode() funcionaria para o acima com bastante facilidade.

Ao falar sobre a inserção de exclusão e atualização. Podemos simplesmente criar uma estrutura de classe como

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;
}

E então você pode ter o principal para parecer

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;
}

A maneira mais fácil é provavelmente criar uma classe pequena que contém seu IP e porta

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

e então crie um list<IpAddress> deles. Em seguida, você pode usar a serialização e a desserialização XML para ler e escrever em um arquivo sua lista.

O .NET BCL não oferece o que você está procurando, pois deseja consultar um arquivo sem carregá -lo na memória primeiro e suportar Adicionar/remover. Então, você teria que rolar seu próprio banco de dados incorporado ou simplesmente usar algo como SQLite http://www.sqlite.org/

IP e Port é um para muitos relacionamentos. Eu consideraria algo assim

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

onde t é uma guia e r n é um retorno de carruagem seguido por uma nova linha

Então, quando você analisa, se você atingir um personagem de guia, sabe tudo o que está nessa linha de lá para a nova linha é um endereço IP, então tudo entre as próximas linhas é um número de porta para esse endereço IP até que você atinja uma guia, Nesse caso, você está em um novo endereço IP. Isso é simples e rápido, mas não como humano legível.

Isso não tem nada a ver com IP e portas. O problema é que, até onde eu sei, o Windows não permite inserir ou remover bytes no/do meio de um arquivo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top