문제

내가 바이트 배열을 나타내는 완전한 TCP/IP 패킷을 전송합니다.정화를 위해 바이트 배열이 주문한 다음과 같다:

(IP 헤더 20 바이트)(TCP 헤더 20 바이트)(페이로-바이트 X)

Parse 기능 허용하는 바이트 배열을 반환합 TCPHeader 체입니다.그것은 다음과 같습니다:

TCPHeader Parse( byte[] buffer );

주어진 원래의 바이트 배열,여기에는 방법이 나는 이 함수를 호출하는 지금.

byte[] tcpbuffer = new byte[ 20 ];
System.Buffer.BlockCopy( packet, 20, tcpbuffer, 0, 20 );
TCPHeader tcp = Parse( tcpbuffer );

이 있는 편리한 방법을 통과하는 TCP 바이트 배열을,즉,바이트 20-39 의 완전한 TCP/IP 패킷을 Parse 기능을 추출하지 않고 그것에 새로운 바이트 배열 첫 번째?

에서는 C++,나서 다음을 수행할 수 있습니다:

TCPHeader tcp = Parse( &packet[ 20 ] );

거기에 아무것도 비슷 C#?내가를 피하려고 작성 및 후속 가비지 컬렉션이 임시 바이트 배열이면 가능합니다.

도움이 되었습니까?

해결책

.NET 프레임 워크에서 볼 수있는 일반적인 관행이며 여기에서 사용하는 것이 좋습니다. 오프셋과 길이를 지정하는 것입니다. 따라서 구문 분석 기능이 전달 된 배열의 오프셋과 사용할 요소 수를 허용하도록하십시오.

물론 C ++에서와 같이 포인터를 전달하는 것처럼 동일한 규칙이 적용됩니다. 배열을 수정해서는 안됩니다. 그렇지 않으면 정확히 데이터가 언제 사용될 것인지 확실하지 않은 경우 정의되지 않은 동작이 발생할 수 있습니다. 그러나 더 이상 배열을 수정하지 않을 경우 문제가되지 않습니다.

다른 팁

나는 통과 할 것이다 ArraySegment<byte> 이 경우.

당신은 당신의 것을 바꿀 것입니다 Parse 이것에 대한 방법 :

// Changed TCPHeader to TcpHeader to adhere to public naming conventions.
TcpHeader Parse(ArraySegment<byte> buffer)

그런 다음 전화를 변경합니다.

// Create the array segment.
ArraySegment<byte> seg = new ArraySegment<byte>(packet, 20, 20);

// Call parse.
TcpHeader header = Parse(seg);

사용 ArraySegment<T> 배열을 복사하지 않으며 생성자에서 경계를 확인합니다 (따라서 잘못된 범위를 지정하지 않도록). 그런 다음 당신은 당신의 변화를 바꿉니다 Parse 방법 세그먼트에 지정된 범위를 사용하는 방법은 괜찮습니다.

전체 바이트 어레이를 수용 할 편의 과부하를 만들 수도 있습니다.

// Accepts full array.
TcpHeader Parse(byte[] buffer)
{
    // Call the overload.
    return Parse(new ArraySegment<byte>(buffer));
}

// Changed TCPHeader to TcpHeader to adhere to public naming conventions.
TcpHeader Parse(ArraySegment<byte> buffer)

는 경우 IEnumerable<byte> 은 허용 입력으로 보다는 오히려 byte[], 고 당신이 사용하는 C#3.0 에,당신은 쓸 수 있다:

tcpbuffer.Skip(20).Take(20);

참고로 이것은 여전히 열거자를 할당하는 인스턴스에서,그래서 당신은 탈출하지 않는 할당을 모두,그리고 소수의 바이트 사이로보다 느리게 할당하는 새로운 배열에 복사하는 바이트다.

나지 않을 것에 대해 너무 걱정할 할당 및 GC 의 작은 임시 배열 솔직히 말해 그러나.니다.NET 쓰레기 수집의 환경은 매우 효율적인에서는 이 형식의 할당 패턴,경우에 특히 배열은 짧은 살았고,그렇지 않으면 당신이 프로파일 그것을 발견 GC 하여 문제가 될때 나는 그것을 쓰기에 가장 직관적으로 해결 성능 문제를 알고 있는 경우 여러분에게는 그것들이 있습니다.

이런 종류의 통제가 정말로 필요하다면, 당신은 unsafe C#의 기능. GC가 이동하지 않도록 포인터를 갖고 고정 할 수 있습니다.

fixed(byte* b = &bytes[20]) {
}

그러나이 관행은 성능 문제가없는 경우 관리 된 코드로 작업하는 데 제안되지 않습니다. 오프셋과 길이를 들어 올릴 수 있습니다 Stream 수업.

파스 () 메소드를 변경할 수 있으면 처리를 시작 해야하는 오프셋을 수락하도록 변경하십시오. tcpheader parse (byte [] 버퍼, int 오프셋);

LINQ를 사용하여 다음과 같은 작업을 수행 할 수 있습니다.

tcpbuffer.Skip(20).Take(20);

그러나 System.buffer.blockcopy / system.array.copy는 아마도 더 효율적 일 것입니다.

이것이 AC 프로그래머에서 AC# 프로그래머로 오는 방법입니다. MemoryStream을 사용하여 스트림으로 변환 한 다음 BinaryReader를 사용하여 데이터의 이진 블록을 분리합니다. 네트워크 주문에서 Little Endian으로 변환하기 위해 두 개의 도우미 기능을 추가해야했습니다. 또한 바이트를 구축하기 위해 []를 보내기 위해모든 케이스를 지정하지 않고 원래 유형으로 객체를 다시 주조하는 방법이 있습니까? 객체 배열에서 바이트로 변환 할 수있는 함수가 있습니다 [].

  Hashtable parse(byte[] buf, int offset )
  {

     Hashtable tcpheader = new Hashtable();

     if(buf.Length < (20+offset)) return tcpheader;

     System.IO.MemoryStream stm = new System.IO.MemoryStream( buf, offset, buf.Length-offset );
     System.IO.BinaryReader rdr = new System.IO.BinaryReader( stm );

     tcpheader["SourcePort"]    = ReadUInt16BigEndian(rdr);
     tcpheader["DestPort"]      = ReadUInt16BigEndian(rdr);
     tcpheader["SeqNum"]        = ReadUInt32BigEndian(rdr);
     tcpheader["AckNum"]        = ReadUInt32BigEndian(rdr);
     tcpheader["Offset"]        = rdr.ReadByte() >> 4;
     tcpheader["Flags"]         = rdr.ReadByte() & 0x3f;
     tcpheader["Window"]        = ReadUInt16BigEndian(rdr);
     tcpheader["Checksum"]      = ReadUInt16BigEndian(rdr);
     tcpheader["UrgentPointer"] = ReadUInt16BigEndian(rdr);

     // ignoring tcp options in header might be dangerous

     return tcpheader;
  } 

  UInt16 ReadUInt16BigEndian(BinaryReader rdr)
  {
     UInt16 res = (UInt16)(rdr.ReadByte());
     res <<= 8;
     res |= rdr.ReadByte();
     return(res);
  }

  UInt32 ReadUInt32BigEndian(BinaryReader rdr)
  {
     UInt32 res = (UInt32)(rdr.ReadByte());
     res <<= 8;
     res |= rdr.ReadByte();
     res <<= 8;
     res |= rdr.ReadByte();
     res <<= 8;
     res |= rdr.ReadByte();
     return(res);
  }

나는 당신이 C#에서 그런 일을 할 수 있다고 생각하지 않습니다. Parse () 함수가 오프셋을 사용하거나 3 바이트 배열을 만들 수 있습니다. 하나는 IP 헤더 용, 하나는 TCP 헤더 및 하나는 페이로드 용입니다.

검증 가능한 코드를 사용하여이를 수행하는 방법은 없습니다. 구문 분석 방법이 ienumerable을 처리 할 수있는 경우u003Cbyte> 그런 다음 LINQ 표현식을 사용할 수 있습니다

TCPHeader tcp = Parse(packet.Skip(20));

대답 한 일부 사람들

tcpbuffer.Skip(20).Take(20);

잘못 했어요. 이것은 훌륭한 솔루션이지만 코드는 다음과 같습니다.

packet.Skip(20).Take(20);

건너 뛰기를 사용하고 메인에서 메소드를 가져와야합니다. 패킷, 그리고 tcpbuffer 게시 한 코드에는 존재하지 않아야합니다. 또한 그때 사용할 필요가 없습니다 System.Buffer.BlockCopy.

자레드 파 거의 정확했지만 그는 테이크 방법을 잊었습니다.

TCPHeader tcp = Parse(packet.Skip(20));

그러나 그는 잘못 이해하지 못했습니다 tcpbuffer. 게시 된 코드의 마지막 줄은 다음과 같습니다.

TCPHeader tcp = Parse(packet.Skip(20).Take(20));

그러나 System.buffer.blockcopy를 사용하려면 어쨌든 Skip and Take를 사용하려면 Steven Robbins가 대답했을 때 성능이 좋을 수도 있기 때문입니다. 구문 분석 기능 할 수 없습니다 다루다 IEnumerable<byte>, 또는 당신은 당신의 게시 된 질문에서 system.buffer.block에 더 익숙합니다. 그냥 tcpbuffer를 만드십시오 로컬이 아닙니다 변수 사적인 또는 보호 또는 공공의 또는 내부 그리고 공전 아니면 아니에요 필드 (즉, 그것은 정의되고 만들어야합니다. 밖의 게시 된 코드가 실행되는 방법). 따라서 tcpbuffer는 만 만들어집니다 한 번, 그리고 그의 값 (바이트)은 system.buffer.blockcopy line에서 게시 한 코드를 전달할 때마다 설정됩니다.

이렇게하면 코드가 다음과 같습니다.

class Program
{
    //Your defined fields, properties, methods, constructors, delegates, events and etc.
    private byte[] tcpbuffer = new byte[20];
    Your unposted method title(arguments/parameters...)
    {
    //Your unposted code before your posted code
    //byte[] tcpbuffer = new byte[ 20 ]; No need anymore! this line can be removed.
    System.Buffer.BlockCopy( packet, 20, this.tcpbuffer, 0, 20 );
    TCPHeader tcp = Parse( this.tcpbuffer );
    //Your unposted code after your posted code
    }
    //Your defined fields, properties, methods, constructors, delegates, events and etc.
}

또는 단순히 필요한 부분 만 :

private byte[] tcpbuffer = new byte[20];
...
{
...
        //byte[] tcpbuffer = new byte[ 20 ]; No need anymore! This line can be removed.
        System.Buffer.BlockCopy( packet, 20, this.tcpbuffer, 0, 20 );
        TCPHeader tcp = Parse( this.tcpbuffer );
...
}

당신이했다면 :

private byte[] tcpbuffer;

대신, 당신은 당신의 생성자를 사용해야합니다.

this.tcpbuffer = new byte[20];

또는

tcpbuffer = new byte[20];

당신은 당신이 입력 할 필요가 없다는 것을 알고 있습니다 이것. tcpbuffer 이전에는 선택 사항이지만 정적으로 정의하면 할 수 없습니다 그렇게. 대신 클래스 이름을 입력 한 다음 도트 '를 입력하거나 그대로 두어야합니다 (필드의 이름을 입력하십시오.

문제를 뒤집고 버퍼를 오버레이하여 비트를 끌어 당기는 클래스를 만들지 않겠습니까?

// member variables
IPHeader ipHeader = new IPHeader();
TCPHeader tcpHeader = new TCPHeader();

// passing in the buffer, an offset and a length allows you
// to move the header over the buffer
ipHeader.SetBuffer( buffer, 0, 20 );

if( ipHeader.Protocol == TCP )
{
    tcpHeader.SetBuffer( buffer, ipHeader.ProtocolOffset, 20 );
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top