My Windows 10 UWP app is calling a WebAPI web service that I have created. I need to send a JPG file from the UWP app to the server so that the server can store it into another application.

I am using using Windows.Web.Http; as recommended for UWP and using Windows Authentication to connect to the server.

When I perform a POST using the following code, I get the IRandomAccessStream does not support the GetInputStreamAt method because it requires cloning error shown below.

I am able to POST HttpStringContent to the same web service and receive the responses without any issue.

The issue is when trying to send a file to the web service using HttpStreamContent.

public async void Upload_FileAsync(string WebServiceURL, string FilePathToUpload)
{

    //prepare HttpStreamContent
    IStorageFile storageFile = await StorageFile.GetFileFromPathAsync(FilePathToUpload);
    IBuffer buffer = await FileIO.ReadBufferAsync(storageFile);
    byte[] bytes = System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(buffer);
    Stream stream = new MemoryStream(bytes);
    Windows.Web.Http.HttpStreamContent streamContent = new Windows.Web.Http.HttpStreamContent(stream.AsInputStream());


    //send request
    var myFilter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
    myFilter.AllowUI = false;
    var client = new Windows.Web.Http.HttpClient(myFilter);
    Windows.Web.Http.HttpResponseMessage result = await client.PostAsync(new Uri(WebServiceURL), streamContent);
    string stringReadResult = await result.Content.ReadAsStringAsync();

}

Full Error:

{System.NotSupportedException: This IRandomAccessStream does not support the GetInputStreamAt method because it requires cloning and this stream does not support cloning. at System.IO.NetFxToWinRtStreamAdapter.ThrowCloningNotSuported(String methodName) at System.IO.NetFxToWinRtStreamAdapter.GetInputStreamAt(UInt64 position) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at }

Please help!

After you get the file and begin to create a HttpStreamContent instance, you can try to use the StorageFile.OpenAsync method to get an IRandomAccessStream object, then put it as the HttpStreamContent object constructor parameter.

The code will be like this, you can have a try.

public async void Upload_FileAsync(string WebServiceURL, string FilePathToUpload)
{

    //prepare HttpStreamContent
    IStorageFile storageFile = await StorageFile.GetFileFromPathAsync(FilePathToUpload);

    //Here is the code we changed
    IRandomAccessStream stream=await storageFile.OpenAsync(FileAccessMode.Read);
    Windows.Web.Http.HttpStreamContent streamContent = new Windows.Web.Http.HttpStreamContent(stream);

    //send request
    var myFilter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
    myFilter.AllowUI = false;
    var client = new Windows.Web.Http.HttpClient(myFilter);
    Windows.Web.Http.HttpResponseMessage result = await client.PostAsync(new Uri(WebServiceURL), streamContent);
    string stringReadResult = await result.Content.ReadAsStringAsync();
}


    선택기를 사용하여 파일 및 폴더 열기

    [ Windows 10의 UWP 앱에 맞게 업데이트되었습니다. Windows 8.x 문서는 보관을 참조하세요. ]

    중요 API

    사용자가 선택기를 조작할 수 있도록 하여 파일 및 폴더에 액세스합니다. FileOpenPicker 및 FileSavePicker 클래스를 사용하여 파일에 액세스하고 FolderPicker 클래스를 사용하여 폴더에 액세스할 수 있습니다.

    참고 전체 샘플에 대해서는 파일 선택기 샘플을 참조하세요.

    필수 조건

    파일 선택기 UI

    파일 선택기는 사용자를 안내하고 파일을 열거나 저장할 때 일관된 환경을 제공하기 위해 정보를 표시합니다.

    이러한 정보에는 다음이 포함됩니다.

    • 현재 위치
    • 사용자가 선택한 항목
    • 사용자가 이동할 수 있는 위치의 트리. 이러한 위치에는 파일 시스템 위치(예: 음악 또는 다운로드 폴더) 파일 선택기 계약을 구현하는 앱(예: 카메라, 사진 및 Microsoft OneDrive)이 포함됩니다.

    메일 앱은 사용자가 첨부 파일을 선택할 수 있도록 파일 선택기를 표시할 수 있습니다.

    두 개의 파일이 열기 위해 선택되어 있는 파일 선택기

    선택기 작동 방식

    선택기를 통해 앱은 사용자 시스템의 파일 및 폴더를 액세스하고 찾아보고 저장할 수 있습니다. 앱은 사용자가 작업할 수 있도록 StorageFile 및 StorageFolder 개체로 이러한 선택을 수신합니다.

    선택기는 통합된 단일 인터페이스를 사용하여 사용자가 파일 시스템이나 다른 앱에서 파일 및 폴더를 선택할 수 있도록 합니다. 다른 앱에서 선택한 파일은 파일 시스템의 파일과 유사하며 StorageFile 개체로 반환됩니다. 일반적으로 앱은 다른 개체와 동일한 방식으로 해당 개체에서 작동할 수 있습니다. 다른 앱에서는 파일 선택기 계약에 참여함으로써 파일을 사용할 수 있게 합니다. 앱에서 다른 앱에 파일, 저장 위치 또는 파일 업데이트를 제공하도록 하려면 파일 선택기 계약과 통합을 참조하세요.

    예를 들어 사용자가 파일을 열 수 있도록 앱에서 파일 선택기를 호출할 수 있습니다. 그러면 해당 앱이 호출 앱이 됩니다. 파일 선택기는 시스템 및/또는 다른 앱과 상호 작용하여 사용자가 파일을 탐색하고 선택할 수 있도록 합니다. 사용자가 파일을 선택하면 파일 선택기는 해당 파일을 앱에 반환합니다. 사용자가 OneDrive와 같은 제공 앱에서 파일을 선택할 경우의 프로세스는 다음과 같습니다.

    한 앱이 파일 선택기를 두 앱 간의 인터페이스로 사용하여 다른 앱에서 열 파일을 가져오는 프로세스를 보여 주는 다이어그램입니다.

    단일 파일 선택: 전체 코드 목록

    CSharp
    var picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
    picker.SuggestedStartLocation =
        Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
    picker.FileTypeFilter.Add(".jpg");
    picker.FileTypeFilter.Add(".jpeg");
    picker.FileTypeFilter.Add(".png");
    
    Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
    if (file != null)
    {
        // Application now has read/write access to the picked file
        this.textBlock.Text = "Picked photo: " + file.Name;
    }
    else
    {
        this.textBlock.Text = "Operation cancelled.";
    }
    

    단일 파일 선택: 단계별

    파일 선택기 사용에는 파일 선택기 개체를 만들고 사용자 지정한 다음 사용자가 하나 이상의 항목을 선택할 수 있도록 파일 선택기를 표시하는 과정이 포함됩니다.

    1. FileOpenPicker 만들기 및 사용자 지정

      CSharp
      var picker = new Windows.Storage.Pickers.FileOpenPicker();
          picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
          picker.SuggestedStartLocation =
              Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
          picker.FileTypeFilter.Add(".jpg");
          picker.FileTypeFilter.Add(".jpeg");
          picker.FileTypeFilter.Add(".png");
      

      사용자 및 앱과 관련된 파일 선택기 개체에서 속성을 설정합니다. 파일 선택기 사용자 지정 방법을 결정하는 데 도움이 되는 지침은 파일 선택기에 대한 지침 및 검사 목록을 참조하세요.

      이 예제에서는 세 가지 속성(ViewMode, SuggestedStartLocation  FileTypeFilter)을 설정하여 사용자가 선택할 수 있는 편리한 위치에 사진의 풍부한 시각적 표시를 만듭니다.

      • ViewMode PickerViewMode Thumbnail 열거 값으로 설정하면 사진 미리 보기를 사용하여 파일 선택기에 파일을 표시함으로써 풍부한 시각적 표시가 만들어집니다. 이렇게 하려면 사진이나 비디오와 같은 시각적 파일을 선택합니다. 그렇지 않으면 PickerViewMode.List를 사용합니다. 사진 또는 비디오 첨부  문서 첨부 기능이 있는 가상 메일 앱은 파일 선택기를 표시하기 전에 기능에 적합한 ViewMode를 설정합니다.

      • PickerLocationId.PicturesLibrary를 사용하여 SuggestedStartLocation을 사진으로 설정하면 사용자가 사진을 찾을 수 있을 것 같은 위치에서 시작됩니다. SuggestedStartLocation을 선택하려는 파일 형식(예: 음악, 사진, 동영상 또는 문서)에 적절한 위치로 설정합니다. 사용자는 시작 위치에서 다른 위치로 이동할 수 있습니다.

      • FileTypeFilter를 사용하여 파일 형식을 지정하면 사용자가 관련 있는 파일을 선택하는 데 집중할 수 있습니다. FileTypeFilter의 이전 파일 형식을 새 항목으로 대체하려면 Add 대신 ReplaceAll을 사용합니다.

    2. FileOpenPicker 표시

      • 단일 파일을 선택하려면
      CSharp
      Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
              if (file != null)
              {
                  // Application now has read/write access to the picked file
                  this.textBlock.Text = "Picked photo: " + file.Name;
              }
              else
              {
                  this.textBlock.Text = "Operation cancelled.";
              }
      
      • 여러 파일을 선택하려면
      CSharp
      var files = await picker.PickMultipleFilesAsync();
              if (files.Count > 0)
              {
                  StringBuilder output = new StringBuilder("Picked files:\n");
      
                  // Application now has read/write access to the picked file(s)
                  foreach (Windows.Storage.StorageFile file in files)
                  {
                      output.Append(file.Name + "\n");
                  }
                  this.textBlock.Text = output.ToString();
              }
              else
              {
                  this.textBlock.Text = "Operation cancelled.";
              }
      

    폴더 선택: 전체 코드 목록

    CSharp
    var folderPicker = new Windows.Storage.Pickers.FolderPicker();
    folderPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.Desktop;
    folderPicker.FileTypeFilter.Add("*");
    
    Windows.Storage.StorageFolder folder = await folderPicker.PickSingleFolderAsync();
    if (folder != null)
    {
        // Application now has read/write access to all contents in the picked folder
        // (including other sub-folder contents)
        Windows.Storage.AccessCache.StorageApplicationPermissions.
        FutureAccessList.AddOrReplace("PickedFolderToken", folder);
        this.textBlock.Text = "Picked folder: " + folder.Name;
    }
    else
    {
        this.textBlock.Text = "Operation cancelled.";
    }
    

     앱이 선택기를 통해 파일 또는 폴더에 액세스할 경우 해당 항목을 앱의 FutureAccessList 또는 MostRecentlyUsedList에 추가하여 추적합니다. 이러한 목록을 사용하는 방법에 대한 자세한 내용은 최근에 사용한 파일 및 폴더를 추적하는 방법을 참조하세요.

    파일 만들기, 쓰기 및 읽기

    [ Windows 10의 UWP 앱에 맞게 업데이트되었습니다. Windows 8.x 문서는 보관을 참조하세요. ]

    중요 API

    StorageFile 개체를 사용하여 파일을 읽고 씁니다.

    참고

    파일 액세스 샘플도 참조하세요.

    필수 조건

    파일 만들기

    앱의 로컬 폴더에 파일을 만드는 방법은 다음과 같습니다. 이미 있는 경우 바꿉니다.

    C#
    // Create sample file; replace if exists.
    Windows.Storage.StorageFolder storageFolder =
        Windows.Storage.ApplicationData.Current.LocalFolder;
    Windows.Storage.StorageFile sampleFile =
        await storageFolder.CreateFileAsync("sample.txt",
            Windows.Storage.CreationCollisionOption.ReplaceExisting);
    
    C++
    // Create a sample file; replace if exists.
    StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
    concurrency::create_task(storageFolder->CreateFileAsync("sample.txt", CreationCollisionOption::ReplaceExisting));
    
    VB
    ' Create sample file; replace if exists.
    Dim storageFolder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
    Dim sampleFile As StorageFile = Await storageFolder.CreateFileAsync("sample.txt", CreationCollisionOption.ReplaceExisting)
    

    파일에 쓰기

    StorageFile 클래스를 사용하여 디스크의 쓰기 가능 파일에 쓰는 방법은 다음과 같습니다. 파일에 쓰는 각 방법의 공통적인 첫 번째 단계(파일을 만든 즉시 해당 파일에 쓰는 경우 제외)는 StorageFolder.GetFileAsync를 사용하여 파일을 가져오는 것입니다.

    C#
    Windows.Storage.StorageFolder storageFolder =
        Windows.Storage.ApplicationData.Current.LocalFolder;
    Windows.Storage.StorageFile sampleFile =
        await storageFolder.GetFileAsync("sample.txt");
    
    C++
    StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
    create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile) 
    {
        // Process file
    });
    
    VB
    Dim storageFolder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
    Dim sampleFile As StorageFile = Await storageFolder.GetFileAsync("sample.txt")
    

    파일에 텍스트 쓰기

    FileIO 클래스의 WriteTextAsync 메서드를 호출하여 파일에 텍스트를 씁니다.

    C#
    await Windows.Storage.FileIO.WriteTextAsync(sampleFile, "Swift as a shadow");
    
    C++
    StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
    create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile) 
    {
        //Write text to a file
        create_task(FileIO::WriteTextAsync(sampleFile, "Swift as a shadow"));
    });
    
    VB
    Await Windows.Storage.FileIO.WriteTextAsync(sampleFile, "Swift as a shadow")
    

    버퍼를 사용하여 파일에 바이트 쓰기(2단계)

    1. 먼저 ConvertStringToBinary를 호출하여 파일에 쓰려는 바이트(임의 문자열 기반)의 버퍼를 가져옵니다.

      C#
      var buffer = Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(
              "What fools these mortals be", Windows.Security.Cryptography.BinaryStringEncoding.Utf8);
      
      C++
      StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
      create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile)
      {
          // Create the buffer
          IBuffer^ buffer = CryptographicBuffer::ConvertStringToBinary
          ("What fools these mortals be", BinaryStringEncoding::Utf8);
      });
      
      VB
      Dim buffer = Windows.Security.Cryptography.CryptographicBuffer.ConvertStringToBinary(
                          "What fools these mortals be",
                          Windows.Security.Cryptography.BinaryStringEncoding.Utf8)
      
    2. 그런 다음 FileIO 클래스의 WriteBufferAsync 메서드를 호출하여 파일에 버퍼의 바이트를 씁니다.

      C#
      await Windows.Storage.FileIO.WriteBufferAsync(sampleFile, buffer);
      
      C++
      StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
      create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile)
      {
          // Create the buffer
          IBuffer^ buffer = CryptographicBuffer::ConvertStringToBinary
          ("What fools these mortals be", BinaryStringEncoding::Utf8);      
          // Write bytes to a file using a buffer
          create_task(FileIO::WriteBufferAsync(sampleFile, buffer));
      });
      
      VB
      Await Windows.Storage.FileIO.WriteBufferAsync(sampleFile, buffer)
      

    스트림을 사용하여 파일에 텍스트 쓰기(4단계)

    1. 먼저 StorageFile.OpenAsync 메서드를 호출하여 파일을 엽니다. 이 메서드는 열기 작업이 완료되면 파일 내용의 스트림을 반환합니다.

      C#
      var stream = await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
      
      C++
      StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
      create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile)
      {
          create_task(sampleFile->OpenAsync(FileAccessMode::ReadWrite)).then([sampleFile](IRandomAccessStream^ stream)
          {
              // Process stream
          });
      });
      
      VB
      Dim stream = Await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite)
      
    2. 다음으로, stream에서 GetOutputStreamAt 메서드를 호출하여 출력 스트림을 가져옵니다. using 문에 이 스트림을 넣어 출력 스트림의 수명을 관리합니다.

      C#
      using (var outputStream = stream.GetOutputStreamAt(0))
      {
          // We'll add more code here in the next step.
      }
      stream.Dispose(); // Or use the stream variable (see previous code snippet) with a using statement as well.
      
      C++
      // Add to "Process stream" in part 1
      IOutputStream^ outputStream = stream->GetOutputStreamAt(0);
      
      VB
      Using outputStream = stream.GetOutputStreamAt(0)
      ' We'll add more code here in the next step.
      End Using
      
    3. 이제 기존 using 문 내에 다음 코드를 추가해 새 DataWriter 개체를 만들고 DataWriter.WriteString 메서드를 호출하여 출력 스트림에 씁니다.

      C#
      using (var dataWriter = new Windows.Storage.Streams.DataWriter(outputStream))
      {
          dataWriter.WriteString("DataWriter has methods to write to various types, such as DataTimeOffset.");
      }
      
      C++
      // Added after code from part 2
      DataWriter^ dataWriter = ref new DataWriter(outputStream);
      dataWriter->WriteString("DataWriter has methods to write to various types, such as DataTimeOffset.");
      
      VB
      Dim dataWriter As New DataWriter(outputStream)
      dataWriter.WriteString("DataWriter has methods to write to various types, such as DataTimeOffset.")
      
    4. 마지막으로, 내부 using 문 내에 다음 코드를 추가하여 StoreAsync로 텍스트를 파일에 저장하고 FlushAsync로 스트림을 닫습니다.

      C#
      await dataWriter.StoreAsync();
          await outputStream.FlushAsync();
      
      C++
      // Added after code from part 3
      dataWriter->StoreAsync();
      outputStream->FlushAsync();
      
      VB
      Await dataWriter.StoreAsync()
          Await outputStream.FlushAsync()
      

    파일에서 읽기

    StorageFile 클래스를 사용하여 디스크의 파일에서 읽는 방법은 다음과 같습니다. 파일에서 읽는 각 방법의 공통적인 첫 번째 단계는 StorageFolder.GetFileAsync를 사용하여 파일을 가져오는 것입니다.

    C#
    Windows.Storage.StorageFolder storageFolder =
        Windows.Storage.ApplicationData.Current.LocalFolder;
    Windows.Storage.StorageFile sampleFile =
        await storageFolder.GetFileAsync("sample.txt");
    
    C++
    StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
    create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile)
    {
        // Process file
    });
    
    VB
    Dim storageFolder As StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder
    Dim sampleFile As StorageFile = Await storageFolder.GetFileAsync("sample.txt")
    

    파일에서 텍스트 읽기

    FileIO 클래스의 ReadTextAsync 메서드를 호출하여 파일에서 텍스트를 읽습니다.

    C#
    string text = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);
    
    C++
    StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
    create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile)
    {
        return FileIO::ReadTextAsync(sampleFile);
    });
    
    VB
    Dim text As String = Await Windows.Storage.FileIO.ReadTextAsync(sampleFile)
    

    버퍼를 사용하여 파일에서 텍스트 읽기(2단계)

    1. 먼저 FileIO 클래스의 ReadBufferAsync 메서드를 호출합니다.

      C#
      var buffer = await Windows.Storage.FileIO.ReadBufferAsync(sampleFile);
      
      C++
      StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
      create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile)
      {
          return FileIO::ReadBufferAsync(sampleFile);
      
      }).then([](Streams::IBuffer^ buffer)
      {
          // Process buffer
      });
      
      VB
      Dim buffer = Await Windows.Storage.FileIO.ReadBufferAsync(sampleFile)
      
    2. 그런 다음 DataReader 개체를 사용하여 버퍼의 길이와 버퍼의 내용을 차례로 읽습니다.

      C#
      using (var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer))
      {
          string text = dataReader.ReadString(buffer.Length);
      }
      
      C++
      // Add to "Process buffer" section from part 1
      auto dataReader = DataReader::FromBuffer(buffer);
      String^ bufferText = dataReader->ReadString(buffer->Length);
      
      VB
      Dim dataReader As DataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer)
      Dim text As String = dataReader.ReadString(buffer.Length)
      

    스트림을 사용하여 파일에서 텍스트 읽기(4단계)

    1. StorageFile.OpenAsync 메서드를 호출하여 파일의 스트림을 엽니다. 이 메서드는 작업이 완료되면 파일 내용의 스트림을 반환합니다.

      C#
      var stream = await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
      
      C++
      StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
      create_task(storageFolder->GetFileAsync("sample.txt")).then([](StorageFile^ sampleFile)
      {
          create_task(sampleFile->OpenAsync(FileAccessMode::Read)).then([sampleFile](IRandomAccessStream^ stream)
          {
              // Process stream
          });
      });
      
      VB
      Dim stream = Await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.Read)
      
    2. 나중에 사용할 스트림의 크기를 가져옵니다.

      C#
      ulong size = stream.Size;
      
      C++
      // Add to "Process stream" from part 1
      UINT64 size = stream->Size;
      
      VB
      Dim size = stream.Size
      
    3. GetInputStreamAt 메서드를 호출하여 입력 스트림을 가져옵니다. 이를 using 문에 배치하여 스트림의 수명을 관리합니다. GetInputStreamAt을 호출할 때 0을 지정하여 위치를 스트림의 시작 부분으로 설정합니다.

      C#
      using (var inputStream = stream.GetInputStreamAt(0))
      {
          // We'll add more code here in the next step.
      }
      
      C++
      // Add after code from part 2
      IInputStream^ inputStream = stream->GetInputStreamAt(0);
      auto dataReader = ref new DataReader(inputStream);
      
      VB
      Using inputStream = stream.GetInputStreamAt(0)
          ' We'll add more code here in the next step.
      End Using
      
    4. 마지막으로, 기존 using 문 내에 다음 코드를 추가하여 스트림에서 DataReader 개체를 가져온 다음 DataReader.LoadAsync  DataReader.ReadString를 호출하여 텍스트를 읽습니다.

      C#
      using (var dataReader = new Windows.Storage.Streams.DataReader(inputStream))
      {
          uint numBytesLoaded = await dataReader.LoadAsync((uint)size);
          string text = dataReader.ReadString(numBytesLoaded);
      }
      
      C++
      // Add after code from part 3
      create_task(dataReader->LoadAsync(size)).then([sampleFile, dataReader](unsigned int numBytesLoaded)
      {
          String^ streamText = dataReader->ReadString(numBytesLoaded);
      });
      
      VB
      Dim dataReader As New DataReader(inputStream)
      Dim numBytesLoaded As UInteger = Await dataReader.LoadAsync(CUInt(size))
      Dim text As String = dataReader.ReadString(numBytesLoaded)





    Niraj Bhatt – Architect's Blog

    Ruminations on .NET, Architecture & Design

    Verint Architecture

    This is an offbeat post. Recently I was attending Verint training which happens to be a COTS product for managing your Call Center requirements. These requirements include the ones like Call Recording, Screen Capture, Quality management of calls / Scoring, Reports, etc. In this post I will jot down the basics of Verint Architecture I could collect from session.

    Verint like any other Call Center Management related product taps into your incoming PSTN network via PBX and CTIinterface. CTI interface raises events on various activities, for instance whenever there is an incoming call to your contact center. From here Verint takes over and starts recording the call. Verint also supports Screen capture in case your business needs one.

    Verint has a tiered architecture in form of – Acquisition Server, Hub & Data Center. Verint distinguishes between Hub & Data Center for high volume scenarios, otherwise it bundles both together into something called AIO (All-In-One) Server.

    Acquisition Server is where modules like VAM (Voice Acquisition Module) & SAM (Screen Acquisition Module) run. Recorded calls are stored here temporarily, and corresponding call detail is captured (Verint calls it as Contact detail). The recorded call files are moved from Acquisition Server to Hub Server via FTP. And the Contact details which are stored in SQL Server are transferred to Hub Server via MSMQ. There is a purging service on Acquisition Server which can purge recorded calls data on disk space available, duration, etc.

    Hub Servers appeal seems to be there from a Latency (performance) standpoint. They get Contact details & recorded voice calls from Acquisition Server. Acquisition server has a relatively small storage and relies on Hub for larger storage. All the recorded voice calls needs to be backed up (archived) from Hub Server itself. But unlike them Contact details are synced with Data Center again via MSMQ. I am not quite sure of the motivation in handling these two differently. Verint Portal is also deployed on Hub Server.

    Data Center Servers are the ones that have entire Contact database available with them. They also have reporting tools, data warehouse, application database (scoring for instance), speech analytics etc. as part of their system. Data Center seems to handling everything apart from recorded calls which are archived at Hub level.


    SNMP

    윤 상배

    dreamyun@yahoo.co.kr

    교정 과정
    교정 0.82003년 4월 20일 21시
    최초 문서작성


    1절. 소개

    개인적으로 최근들어 SNMP에 관심을 가지게 되었다. (실은 상당히 오래되었지만) 그래서 앞으로 몇부? 에 걸쳐서 SNMP관련 강좌를 개설하고자 한다. 강좌는 SNMP개요및 설치운용에서 부터 시작해서 프로그래밍을 통해서 SNMP응용 애플리케이션을 제작하고, 확장 MIB(뒤에 설명한다)를 작성하는 것 까지를 다룰것이다.

    이번글은 그중 첫번째 글로 SNMP개요와 설치및 운용에 대한 글이다. 설치및 운용은 실제 어떻게 작동되는지 눈으로 확인하는 차원의 수준에서 이루어질 것이며, 설치되는 snmp애플리케이션의 상세설치와 높은 수준에서의 운용에 대해서는 언급하지 않을것이다. 이러한 것들은 (필요할경우)해당 snmp애플리케이션의 메뉴얼을 참고해서 개인적으로 학습해야만 할것이다.

    여기에서 얻은 지식은 나중에 SNMP애플리케이션을 제작하는 밑거름이 될것이다.


    2절. SNMP개요

    2.1절. SNMP란 무엇인가

    SNMP는 Simple Network Management Protocol의 약자이다. 해석을 해보자면 간단한 네트워크관리를 위한 규약 인데, 말그대로 SNMP는 네트워크관리를 위한 용도로 사용되는 프로토콜이다. 가장 앞에 Simple라는 단어가 붙어있는데, 진짜로 간단한 프로토콜인지 아닌지는 사람에 따라 약간씩 차이가 있을수 있다. 필자가 보기엔 그리 복잡한 프로토콜은 아닌것 같은데, 어떤 사람들은 매우 복잡한 프로토콜 이라고 말하는 사람들도 있다.

    그럼 먼저 SNMP가 나타난 배경에 대해서 알아보도록 하겠다. SNMP가 쓰이기 전에 일반적으로 사용되는 네트워크 관리는 ICMP에 의존했었다. ICMP는 Network계층의 프로토콜로써, 운영체제에 관계없이 사용할수 있는 간단한 프로토콜이였다. 이 프로토콜을 이용해서 우리는 네트워크로 연결된 각각의 호스트가 작동하고 있는지, 작동한다면 어느정도의 응답시간을 가지고 작동하는지 등의 간단한 정보를 얻을수 있었으며, 초기에는 이정도로도 필요한 네트워크 관리가 가능했었다. ICMP를 이용한 가장 유용한 도구는 아마도 ping 프로그램일 것이다.

    그러나 인터넷의 사용이 보편화되고 네트워크에 연결된 호스트의 수가 증가하자 거기에 따라서 네트워크 구성역시 복잡해지고, ICMP만을 가지고는 이러한 네트워크의 관리를 효율적으로 할수 없게 되었다.

    그래서 몇가지 프로토콜에 대한 연구가 진행되었고, SGMP, HIMS, CMIP/CMIS등이 제안되게 되었다. 이중에서 SGMP를 발전시킨 SNMP가 사실상 네트워크 관리를 위한 표준적인 프로토콜로 자리잡게 되었다. 다른 프로토콜들이 사용되지 않은데에는 몇가지 이유가 있었다. CMIP/CMIS는 너무 방대하고 너무 복잡했으며, HEMS의 경우에는 실제 적용사례가 적었기 때문이다.

    어쨋든 SNMP는 거의 대부분의 운영체제에서 사용되어 지고 있다. 여러분이 사용하는 Linux, 그밖의 대부분의 유닉스와, 윈도우계열 운영체제는 기본적으로 SNMP프로토콜을 사용하는 도구들을 제공하고 있다. 그외에도 router등 TCP/IP를 네트워크 프로토콜로 사용되는 운영체제들 역시 SNMP는 필수적으로 제공하고 있다.


    2.2절. SNMP로 할수 있는 것들

    SNMP를 이용해서 할수 있는 것들은 다음과 같다.

    네트워크 구성관리

    네트워크상의 호스트들이 어떤 구조를 이루고 있는지 지도를 그리는게 가능하다.

    성능관리

    각 네트워크 세그먼트간 네트워크 사용량, 에러량, 처리속도, 응답시간 등 성능 분석에 필요한 통계정보를 얻어낼수 있다.

    장비관리

    SNMP의 주목적이 네트워크관리관리 이기는 하지만 SNMP특유의 유연한 확장성을 이용하여서 시스템정보(CPU, MEMORY, DISK 사용량)의 정보를 얻어올 수 있도록 많은 부분이 확장되었다. 이 정보는 네트워크문제를 해결하는데 큰도움을 준다. 예를들어 특정 세그먼트의 네트워크 사용량이 갑자기 급증했는데, 특정 호스트의 CPU사용율까지 갑자기 증가했다면, 우리는 해당 호스트에서 문제가 발생했을것이란걸 유추해낼수 있을것이다.

    보안관리

    정보의 제어 및 보호 기능, 최근버젼인 SNMP3는 특히 정보보호를 위한 기능이 향상되었다.


    2.3절. SNMP를 통한 망의 구성

    SMTP는 인터넷상에서 메시지를 교환하기 위한 프로토콜로 사용되며, 주로 전자메일 교환을 위해서 사용되는 프로토콜이다. 그러나 SMTP는 어디까지나 프로토콜일 뿐이며, 실제 메시지를 인터넷상에서 주고 받기 위해서는 SMTP프로토콜을 사용하는 SMTP서버(Sendmail같은)와 SMTP클라이언트(mutt, pine같은)가 준비되어 있어야만 한다.

    SNMP역시 그자체로는 프로토콜일 뿐이며 SNMP프로토콜을 활용해서 실제 네트워크 관리 정보를 얻어오기 위해서는 응용 애플리케이션이 준비되어있어야만 한다. 보통의 네트워크프로토콜을 사용하는 애플리케이션이 서버/클라이언트 모델로 구성되듯이 SNMP역시 서버와 클라이언트로 구성된다.

    그림 1. SNMP망 관리 시스템

    일반적으로 SNMP망 에서는 서버/클라이언트라고 부르지 않고 snmp manager/snmp agent라고 부른다. snmp agent는 관리대상이 되는 시스템에 설치되어서 필요한 정보(네트워크 혹은 시스템)를 수집하기 위한 snmp 모듈(혹은 애플리케이션) 이며, snmp manager은 snmp agent가 설치된 시스템에 필요한 정보를 요청하는 snmp 모듈이다. snmp agent는 서버, snmp manager은 클라이언트로 생각하면 이해하기가 좀더 수월할 것이다(그러나 반드시 agent가 서버, manager이 클라이언트가 되는건 아니다. 그냥 개념적으로 이해만 하고 있도록 하자).


    2.4절. MIB에 대해서

    SNMP는 네트워크를 관리하기 위한 프로토콜이다. 그렇다면 무엇을 관리할 것인가(관리객체)를 결정해야 할것이다. 관리객체를 결정했다면, 이러한 관리객체를 효과적으로 관리하기 위해서 이를 분류해야 할것이다. 이게 바로 MIB이다.

    MIB는 Man In Black의 줄임말이 아니다. Management Information Base의 줄임말인데, 관리되어야할 자원 객체의 분류된 정보를 말한다. 관리되어야할 객체는 시스템정보, 네트워크사용량, 네트워크 인터페이스정보 등이 된다.

    이 MIB객체들은 관리하기 편하도록 Tree구조를 가지게 된다. 다음은 MIB의 일반적인 구조이다.

    그림 2. MIB계층 구조

    MIB는 위에서 처럼 계층적인(디렉토리) 구조를 가지게 된다(위의 그림은 MIB를 설명하기 위해 일부만을 표시하고 있다). 예를들어서 agent가 설치되어 있는 시스템으로 부터 시스템부가정보(sysDescr)를 얻어오길 원한다면 ISO.org.dod.internet.mgmt.mib-2.system.sysDescr과 같은 식으로 manger에서 데이타를 요청하면 된다.

    위의 MIB계층 구조를 보면 각 MIB옆에 숫자가 있는것을 볼수 있다. 이 숫자가 OID번호이다. 즉 sysDescr의 OID값은 1.3.6.1.1.2.1.1.1 이 될것이다. OID번호를 이용하는 이유는 MIB고유 문자열을 통해서 원하는 데이타를 가져오기위해서는 아무래도 요청이 길어질수가 있기 때문이다.

    MIB는 IANA(Internet Assigned Number Authority)라는 단체에서 관리하며 표준적으로 사용되고 있다. 그럼으로 표준적인 MIB구현을 위해서는 IANA에서 OID를 부여받아야만 한다. 그래야 전체네트워크상에서 다른 여러가지 MIB와 중복되지 않고 사용이 가능할것이다.

    작은 정보: cisco과 같은 대중적인(거의 표준이나 마찬가지인) 제품들은 모두 자체적인 MIB를 구현해서 IANA에 등록하여 사용하고 있다. 여러분이 cisco 라우터등의 SNMP정보를 접근할수 있다면 cisco MIB가 등록되어 있음을 확인할수 있을것이다. 확인하는 방법은 다음 강좌에서 따로 언급하도록 하겠다.

    MIB는 계층적 구조를 가짐으로 필요에 따라서 확장해서 사용이 가능하며, (물론 프로그래밍 능력이 있어야 하지만)때에 따라서는 자체 회사내에서만 사용가능하거나 제한된 네트워크 영역의 네트워크상황을 관제하는 제품을 위한 MIB를 추가해야 하는경우가 생길수 있을것이다. 그래서 사설로 MIB를 만들어서 사용할수 있는 여지를 남겨두었다. (마치 독립된 지역네트워크를 위해 사설IP를 사용하는 것처럼) 이러한 사설 MIB는 private(4)의 enterprises(1)에 정의해서 사용할수 있다. 여러분이 그리 대중적이지 않은 그래서 IANA에 등록되지 않은 어떤 장비의 고유 SNMP정보를 얻어오고 싶다면 업체에 문의하거나, 메뉴얼을 확인하는 정도로 쉽게 SNMP정보를 얻어올수 있다.

    현재 MIB는 버젼 2까지나와 있으며, 버젼의 구분을 위해서 MIB-1, MIB-2로 부르고 있다. MIB-2는 MIB-1의 확장판으로 MIB-1의 모든 객체를 포함하여 약 171개의 객체들을 더 포함하고 있다. 최근의 제품들은 대부분 MIB-2를 지원하고 있다. 물론 위에서 말했듯이 독자적인 MIB를 만들어서 사용할수 있으며, 이를 확장 MIB라고 부른다.


    2.5절. SNMP 프로토콜의 동작과 구성

    현재 SNMP는 버전 3가지 나와있는 상태이지만 아직까지는 버젼2가 가장 널리 사용 되고 있다. 필자역시 SNMP 버젼 2에 대한 경험이 많은 관계로 버젼2를 기준으로 설명하도록 하겠다.

    SNMP는 기본적으로 네트워크 정보를 수집하는데 그 목적이 있는데, 수집하는 몇가지 각각 다른 방법이 있다. 일반적으로 생각해서 우리가 생활중에 얻게 되는 정보는 우리가 요구해서 발생하는 정보와(신문을 구입한다든지, 인터넷으로 서핑을 하는등) 뉴스속보와 같은 형식으로 중요한 일이 있을때 발생하는 정보가 있을것이다. 또한 단지 정보를 얻는데 그치지 않고 정보를 입력하기도 한다.

    SNMP정보수집역시 기본적으로 위의 일상생활에서의 정보수집과 같은 방식으로 이루어진다. 이하 snmp manager은 manager로 snmp agent는 agent로 부르도록 한다.

    GET

    manager에서 agent로 특정 정보를 요청하기 위해서 사용한다.

    GET NEXT

    기본적으로는 GET과 같은일을 한다. 그러나 SNMP에서 각정보들은 계층적 구조로 관리된다. 위의 MIB계층 구조를 나타낸 이미지에서 우리는 system(1)계층밑에 있는 모든 정보를 가져오고 싶을 때가 있을것이다. 그럴경우 GET NEXT를 사용할수 있다.

    SET

    manager에서 agent로 특정 값을 설정하기 위해서 사용한다.

    TRAP

    agent에서 통보해야될 어떤 정보가 발생했을때(임계치를 넘는네트워크자원 사용등) manager에게 해당 상황을 알리기 위해서 사용한다. 위의 다른 요청들이 동기적 요청이라면 이것은 비동기적 사건을 알리기 위해서 사용되어진다.

    SNMP프로토콜은 기본적으로 어떤 정보를 요청하는 메시지와 이에 대한 응답메시지로 이루어지며 다음과 같은 구조를 가지고 있다.

    표 1. SNMP 메시지

    VersionCommunity nameSNMP PDU
    Version은 말이 필요없다. SNMP프로토콜의 버젼번호를 나타낸다. Community name은 메니저와 에이전트간의 관계를 나타내는데, 인증, 접근통제등의 목적으로 사용된다. 보통은 간단하게 public을 사용한다. PDU 는 Physical Data Unit의 줄임말인데, 실제 전송되는 필요한 정보들을 담고 있는 Unit이다. Unit 이라고 하는 이유는 실제 전송되는 정보들의 부가 속성을 나타내기 위한 몇가지 값들을 포함하고 있기 때문이다. PDU는 PDU 타입(GET인지 Set인지 GET Next인지, TRAP인지등)과, Request-id, 실제보내고자 하는 데이타등(OID와 OID에 대한 값들)으로 구성되어 있다.

    SNMP를 통해서 전달되는 메시지들은 기본적으로 UDP를 이용하게 된다. 바로위에서 PDU는 Request-id를 포함하고 있다고 했는데, 데이타그램처리방식인 UDP의 단점을 극복하기 위해서 사용되는 값으로, 각 메시지의 요청번호를 표시한다. 그래야만 수신된 SNMP메시지가 어떤 요청에 대해서 수신된 메시지인지 확인이 가능할것이기 때문이다.


    3절. SNMP 설치 및 운용

    그럼 실제로 시스템에 SNMP(agent와 manager 애플리케이션)을 설치해서 정보를 가져오는걸 간단히 테스트 해보도록 하겠다.

    설치는 Linux(Kernel-2.4.x)에서 ucd-snmp로 할것이다. 위에서 설명했듯이, SNMP는 manager과 agent로 운영되게 되는데, 테스트의 편의를 위해서 하나의 시스템(localhost)에서 manager와 agent를 운용하도록 하겠다.


    3.1절. ucd-snmp 설치

    ucd-snmp는 net-snmp.sourceforge.net에서 얻을수 있으며 애플리케이션 관련 정보들도 얻을수 있다. ucd-snmp는 현재 버젼 5.x대까지 진행되어 있는데, 5.x부터는 net-snmp로 이름을 바꾸고 개발되어지고 있으며, 4.x버젼까지를 ucd-snmp라고 부르고 있다. 필자는 익숙한 ucd-snmp(버젼 4.x)를 설치하도록 할것이다. 비록 net-snmp가 최신이긴 하지만 별로 다루어본적이 없고, 대부분의 경우 아직까지는 ucd-snmp가 많이 사용되어지고 있기 때문이다. 최신이 아니라고 불만을 가질 필요는 없다. 근본적으로 net-snmp와 ucd-snmp간의 차이는 없으며, 우리의 목적은 최신의 snmp애플리케이션을 테스트하는게 아닌 snmp의 기능과 원리를 이해하고 이를 이용해서 필요한 응용 애플리케이션을 작성하는 것이기 때문이다.

    위의 URL에서 ucd-snmp를 다운받아서 압축을 풀고 컴파일 하도록 하자. 컴파일 하는중에는 아마도 아무런 문제가 없을것이다. 컴파일은 매우 일반적인 방법을 따른다. 적당한 디렉토리에 압축을 풀고 ./configure, make, make install 하면된다.

    	
    [root@localhost src]# tar -xvzf ucd-snmp-4.2.6.tar.gz 
    [root@localhost src]# cd ucd-snmp-4.2.6 
    [root@localhost ucd-snmp-4.2.6]# ./configure  
    [root@localhost ucd-snmp-4.2.6]# make 
    [root@localhost ucd-snmp-4.2.6]# make install
    			
    헤에... 너무 간단하지 않은가 ?


    3.2절. SNMP AGENT 실행

    make install 까지 했다면 agent와 manager프로그램이 모두 설치되어 있을 것이다. 그리고 여기에 더불어 개발자를 위한 각종 라이브러리와 헤더파일들도 설치된다. 이 라이브러리와 헤더파일들은 개발할때 필요하며 다음 강좌에서 다루게 될것이다.

    ucd-snmp는 agent 프로그램으로 snmpd를 제공한다. agent환경을 제대로 만들려면 복잡해보이는(사실은 그리 복잡하다고 볼수없는) 설정파일을 만들어줘야 하지만 이것은 각자의 몫이다. net-snmp프로젝트 홈페이지에서 제공하는 메뉴얼을 참고하기 바란다. 어쨋든 현재로써는 단지 snmpd를 띄우는 정도로 snmp agent환경을 만들수 있다.

    [root@localhost root]# snmpd
    			
    이것으로 snmp를 테스트할 최소한의 agent환경이 구축되었다.


    3.3절. SNMP MANAGER 테스트

    3.3.1절. 동기적인 데이타 요청 - snmp get, get next

    GET과 GET NEXT는 동기적인 정보요청을 위해서 사용한다. manager에서 agent에 대해서 정보를 요청했을때 해당 정보를 agent에서 보내주는 방식이다. GET은 단일정보요청을 위해서 사용하며, GET NEXT는 해당 계층의 하위에 있는 모든 정보의 요청을 위해서 사용된다.

    ucd-snmp는 이러한 정보요청을 위한 manager프로그램으로 snmpget과 snmpnextsnmpwalk를 제공한다.

    snmpget은 이름에서 알수 있듯이 agent로부터 특정한 정보를 얻어내기 위해서 사용한다. 정보를 얻기 위해 필요한 기본정보는 agent가 설치되어 있는 서버의 주소(혹은 이름) 와 커뮤니티(권한을 위한)이름 그리고 얻기 원하는 정보의 OID번호 혹은 MIB의 계층이름이다. 예를들어서 localhost로부터 public권한을 가지고 sysDescr(시스템 부가정보)정보를 얻어오고 싶다면 아래와 같이 하면 된다.

     
    [root@localhost /root]# snmpget localhost public system.sysDescr.0
    system.sysDescr.0 = Linux localhost 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686
    				
    혹은 MIB이름대신에 OID번호를 사용해도 된다.
     
    [root@localhost /root]# snmpget localhost public 1.1.0 
    system.sysDescr.0 = Linux localhost 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686
    				

    snmpwalk는 해당 MIB의 하위계층에 있는 모든 정보를 요청한다. 예를들어 system MIB의 하위 계층에 있는 모든 OID에 대한 정보를 요청하길 원한다면 아래와 같이 하면된다. 이게 가능한 이유는 snmpwalk가 정보를 요청하기 위해서 snmp메시지를 만들때 PDU타입을 GET NEXT를 사용하기 때문이다. 나중에 직접구현하게 될것이다. 지금은 구현에 신경쓰지 말자.

    [root@localhost /root]# snmpwalk localhost public system
    system.sysDescr.0 = Linux localhost 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686
    system.sysObjectID.0 = OID: enterprises.ucdavis.ucdSnmpAgent.linux
    system.sysUpTime.0 = Timeticks: (2685699) 7:27:36.99
    system.sysContact.0 = yundream@joinc.co.kr
    system.sysName.0 = localhost
    system.sysLocation.0 = myhome 
    system.sysORLastChange.0 = Timeticks: (0) 0:00:00.00
    ....
    				
    system하위의 모든 OID에 대한 정보를 얻어오고 있음을 확인할수 있다.

    snmpgetnext는 snmpwalk의 기능 축소판정도로 볼수 있을것이다. 즉 MIB계층구조에서 현재 요청한 OID의 다음 OID의 정보를 가져온다. 예를들어 system.sysDescr.0에 대한 정보를 요청하면 다음 OID인 system.sysObjectID.0의 정보를 요청하게 될것이다. 이게 가능한 이유는 snmpwalk와 마찬가지로 내부적으로 GET NEXT를 이용하고 있기 때문이다. snmpwalk가 더이상 얻을수 없을때까지 OID를 요청하는것과 달리 snmpgetnext 바로다음의 OID만을 요청한다.

     
    [root@localhost /root]# snmpgetnext localhost public system system.sysDescr.0
    system.sysDescr.0 = Linux localhost 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686
    system.sysObjectID.0 = OID: enterprises.ucdavis.ucdSnmpAgent.linux
    				


    3.3.2절. 비동기적인 데이타 요청 - snmp trap

    기본적으로 GET, GET NEXT를 통한 데이타요청은 일정한 polling시간을 가지고 manager에서 agent로 필요한 정보를 요청하는 방식이다. 그러나 이걸 이용해서는 비동기적으로 발생하는 정보를 수집할수가 없다.

    이러한 비동기적인 정보는 여러가지가 될수 있다. 예를들면 특정 네트워크 세그먼트에 문제가 생겼다거나 디스크나 메모리용량을 과다하게 사용하고 있다거나(많은 운영체제의 경우 시스템정보까지도 snmp를 통해서 얻을수 있도록 허용하고 있다)하는 사건들은 비동기적으로 발생할것이다. 이럴경우에는 agent에서 manager측으로 사건을 통보해야 할것이다. 이렇게 agent에서 manager측으로 비동기적으로 사건을 통보하는 것을 SNMP TRAP라고 한다(간단히 말해서 경고메시지 보내는거다).

    ucd-snmp에서는 이러한 trap정보를 전송하고 받기 위해서 snmptrapd와 snmptrap를 제공한다. snmptrapd는 agent에 제공되는 데몬프로그램으로 manager에서의 trap데이타 발생을 기다린다. snmptrap는 agent에 설치되어서 사용될수 있으며 trap데이타를 manager로 전송하는 일을한다.

    이 snmptrap은 꽤 유용하게 사용할수 있다. 간단하게 스크립트로 만들어서 어떤 파일이 변조되었을경우 trap정보를 manager쪽으로 발생시킨다거나, 프로세스 갯수가 일정갯수 이상 초과했을때 이를 전송한다든지 하는 기능을 비교적 간단하게 추가시킬수 있을것이다.

    다음은 ucd-snmp에서 제공하는 trap애플리케이션을 이용한 간단한 테스트이다. 먼저 snmptrapd를 manager측에서 실행시켜야 한다. 이 애플리케이션은 옵션없이 실행할경우 데몬모드로 실행되며 표준출력을 시키지 않음으로 다음과 같이 옵션을 주고 실행시켜서 일반모드(forground)에서 받은 trap정보를 표준출력하도록 실행시키도록 하자.

    [root@localhost root]# snmptrapd -f -P
    2003-04-23 00:13:34 UCD-snmp version 4.2.6 Started.
    				
    이제 agent측에서 snmptrap를 이용해서 trap정보를 manager로 전송해보도록 하자.
    [root@localhost root]# snmptrap -v 2c -c public localhost "" ucdStart sysContact.0 s "yundream"
    				
    그러면 manager로 system.sysContact.0="yundream" 과 같은 정보가 전달되는걸 확인할수 있을것이다.

    이들 ucd-snmp에서 제공하는 애플리케이션들의 자세한 사용법은 메뉴얼 페이지를 참고하기 바란다.


    4절. 결론

    이상 SNMP의 개념과 개념의 이해를 위해서 실제 사용되는 snmp애플리케이션을 설치해서 간단히 운영테스트까지 해보았다. 이러한 운영테스트를 위해서 ucd-snmp를 사용했는데, 다음 강좌는 ucd-snmp에서 제공하는 snmplib를 통해서 snmp애플리케이션을 만드는 법을 다루도록 하겠다.


    출처 : http://perfectcrimelab.com/136

    이곳저곳에서 본 내용을 짜집기 해서 만든 내용입니다.  
    밑에 참고했던 내용들을 달아놨으니, 혹시 필요하시면 찾아가 보시기 바랍니다.

     

    1. C++로 코드 작성하기

     1) 코드 바로 작성하기 예제

     #include <stdio.h>

    extern "C"
    {
      __declspec(dllexport) void DisplayHelloFromDLL()
      {

        // 여기에 내용내용내용
        printf ("Hello from DLL !\n");
      }
    }

     2) 선언과 정의를 따로 분리하실경우, 선언은 다음과 같이

     extern "C" __declspec(dllimport) void Start_DebugView();

     

    2. C++ 프로젝트 Property Setting


    먼저 , C++로 만든 프로젝트에서 다음과 같이 설정을 해 줍니다. (꼭)

    다음과 같이 설정해주시지 않으면, C#에서 DLL을 Import하여도 실제로 Lo

    출처 : 훈스닷넷, 밑에 참고 참고
    ad가 되지 않습니다... 
    (그러면 삽질을 하게되요... ... 누구처럼??)

     

     

    3. C#에서 사용하기

    C++로 만든 dll은 기존의 C#의 dll을 사용하던 것 처럼 reference에 추가해서 사용하실 수 없습니다.
    그래서 코드에 직접 , 사용한다고 선언해주고 사용하여야 합니다.

    먼저, C++을 이용해서 만든 dll을 필요한 위치에 위치 시킵니다. 저같은 경우 실행파일과 같은 위치에 위치하였습니다. (*)
    그런 뒤 ,

    1 ) using System.Runtime.InteropServices;    NameSpace를 추가한 후
    2 ) [DllImport("TestLib.dll")] 를 이용하여 사용할 Dll을 명시한 후 사용할 함수를 선언합니다.
    (* 실행파일과 같은 위치에 있으면 dll의 이름만 쓰시면 됩니다. 아닌경우 주소까지 같이 명시)
    3) 그리고 그냥 함수처럼 사용하시면 됩니다.

     using System;
    using System.Runtime.InteropServices;     // DLL support

    class HelloWorld
    {
        [DllImport("TestLib.dll")]
        public static extern void DisplayHelloFromDLL ();

        static void Main ()
        {
            Console.WriteLine ("This is C# program");
            DisplayHelloFromDLL ();
        }
    }

     

     

    * 줏은 Tip

    이래저래 귀찮다.. 하시면 
    엔진은 CLR을 사용하지 않고 Native Code로 컴파일하고 엔진을 Wrapping 하는 CLR C++ 프로젝트를 하나 더 만들고,
     그 프로젝트를 참조하여 C#에서 사용하는 방법이 있습니다. 
    주의 ) 어쩌면 더 귀찮아 지실수도... 있습니다.
    * 하지만 약간의 디버깅이 가능하고, 밑의 파라미터 관련하여 포팅도 가능합니다!!!

     

     

    * 한 가지 추가

     C++에서 사용하는 클래스와, C#에서 사용하는 클래스가 다르기 때문에, 파라미터 사용에 대해서 약간의 포팅이 필요합니다. 
    예를들면 다음과 같습니다.

     1) C++ 선언

      HWND FindWindow(LPCSTR swClassName, LPCSTR swTitle);

    2) C# 선언

     [DllImport(“user32.dll”)]     public static extern int FindWindow(string a, string b);

     설명 )

    HWND는 윈도우 핸들을 표현하는 32비트 정수 이므로, int형으로 치환되고 LPCSTR 형은 NULL로 끝나는 문자열을 표현합니다.

    이때 PInvoke string을 자동으로 LPCSTR로 치환해 주는 역할을 하게 됩니다.

     

    WIN32 데이터형의 치환

    Win32 API에서 일반적으로 사용하고 있는 데이터형은 모두 C#의 데이터 형으로 치환될 수 있습니다.

     

    Win32 API TYPE

    C#

    BOOL, BOOLEAN

    bool

    BYTE

    byte

    CALLBACK

    delegate

    COLORREF

    int

    DWORD

    int

    DWORD_PTR

    long

    DWORD32

    uint

    DWORD64

    ulong

    FLOAT

    float

    HACCEL

    int

    HANDLE

    int

    HBITMAP

    int

    HBRUSH

    int

    HCONV

    int

    (모든 HANDLE 타입) Hxxxx

    int

    LPARAM

    long

    LPCSTR

    [in] string [out] StringBuilder

    LPBOOL

    ref bool

    이외 LP* 

    ref 형식

    UINT

    uint

    Uxxxx

    unsigned 타입들..

    WORD

    Short

    WPARAM

    Uint

     

    단, String의 경우는 인코딩과 관련하여 약간 까다롭습니다..
    C++의 경우 char가 1byte, C#의 경우 char가 2byte이기 때문에 ,,,,
    위에서 LPCSTR 가 [in] string [out] StringBuilder와 바로 호환이 된다고해서 좋다고 사용하였다가, 인코딩이 다 깨져서 낭패를 봤습니다..... ㅠ_ㅠ (거짓말쟁이)

    이 경우는 다음과 같이 해결하였습니다.

     

    1) C++ 선언

      extern "C" __declspec(dllexport) void Save_DebugView_Path(char* Save_Path);

     2) C# 선언

     [DllImport("OutputDebugString_HookingDll.dll")]  
     public static extern void Save_DebugView_Path([System.Runtime.InteropServices.InAttribute()] 
                [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string
     pFileName);

     

    다음과 같이 사용하면, 
    (한글을 사용하지 않는 이상) 글자가 깨지지 않고 잘 전달되는 모습을 확인하실 수 있습니다..
    Attribute에서 명시적으로 String에 대해 LPWStr로 변환해서 넘겨주도록 지정하고 있기 때문이에요.
    아래와 같은 과정이 자동으로 수행된다 생각하셔도 되겠습니다.
      

      string Path = @"c:\test.txt";
    IntPtr PathPtr = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(Path);
    // ~~~~~~~~~~~~~~~~~~~~
    //Char *PathPointer = (Char*)PathPtr.ToPointer();
    // ~~~~~~~~~~~~~~~~~~~~
    System.Runtime.InteropServices.Marshal.FreeHGlobal(PathPtr);

    ... 네 그렇습니다..

    그 외의 특별한 경우는 밑에 참고를 잘 뒤져보시면 몇몇개 나옵니다.. 될지안될지는 못믿겠습니다...ㅠㅠ(위에서 한개 안되니까 ... 장담하긴어렵네요..)

     

    이상으로 마칩니다. 감사합니다.

     

    참고)

    http://snipplr.com/view/12509/pass-a-string-from-c-to-a-c-dll-in-net-and-get-a-pointer-to-the-strings-chars/

    http://www.codeproject.com/KB/cs/usecdlllibincs.aspx
    http://www.mdfo.kr/tag/UnmanagedType
    http://sonic.tistory.com/entry/CC%EB%A1%9C-%EB%A7%8C%EB%93%A0-DLL-Library%EB%A5%BC-C%EC%97%90%EC%84%9C-%EC%93%B0%EA%B8%B0

    http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=17&MAEULNO=8&no=113249&ref=113249&page=31

    http://hoons.kr/Board.aspx?Name=QACSHAP&Mode=2&BoardIdx=24982&Key=&Value=
    http://www.hoons.kr/Board.aspx?Name=cshaptip&Mode=2&BoardIdx=21751&Key=&Value=
    http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040102&docId=67700148&qb=U3RydWN0TGF5b3V0



    응용개발자의 자료저장소 __ [펌] C++로 만든 DLL 을 C#에서 사용하기.pdf





    Pocket_ base64 인코딩 &amp; 디코딩 원리.pdf


    base64 인코딩 & 디코딩 원리

    Base64 인코딩은 64개의 문자를 이용하여 바이너리 데이터를 아스키 텍스트 데이터로 표현하기 위해 사용됩니다.

    base64는 8bit의 데이터(바이너리)를 6bit의 크기로 표현합니다. 따라서 24bit를 단위로 하여 3개의 문자에서 4개의 문자를 얻게 되는 것입니다.

    위와 같이 abc를 base64 인코딩 하여 YWJj를 얻을 수 있게 됩니다. 여기서 6bit의 이진수는 아래의 base64 table을 이용하여 문자로 바꿔줍니다.

    그리고 base64 인코딩 24bit 단위인데 인코딩할 문자가 3개(24bit) 단위가 아닐 때는 어떻게 되는지 알아보겠습니다.

    위의 결과를 보면 a라는 문자 하나를 넣었을 때는 YQ==으로 base64 table에 없는 '=' 문자가 추가된 것을 알 수 있습니다. '='은 bit수를 맞춰주기 위해 0으로 채워주는 패딩이라는 것 입니다.

    그럼 문자의 개수가 3n+1개 일 때는 '='이 2개가 될 것이고, 3n+2개 일 때는 '='이 1개가 되는 것을 생각해 볼 수 있습니다.

     secuinside2012 IU 문제를 보고 base64의 decoding시 충돌이 있는 것을 알게 되었는데요 -

    예를 들어 위에서 'a'를 base64인코딩 하여 YQ==를 얻었습니다. 따라서 YQ==를 디코딩하면 'a'를 얻게 됩니다.

    그런데 YR==, YS==, YT== 등 을 디코딩 해보면 모두 'a'가 나오는 것을 볼 수 있습니다.

    아래 그림을 보면서 생각해보면 왜 그런지 알수 있습니다.

    여기서 '='이 2개 이므로 디코딩시 문자가 3n+1개 라는 것을 알 수 있습니다. 그러면 Q(16)을 디코딩시 010000에서 패딩으로 채워진 0000의 4bit는 0이든 1이든 관계가 없다는 것 입니다.

    따라서 3n+1개의 문자일 때는 충돌 가지수가 2의 4승인 16개가 생길 것이고, 3n+2개의 문자일 때는 2의 2승인 4개가 생기고, 3n개의 문자일 때는 충돌이 생기지 않게 됩니다.

    ▶ base64 인코딩, 디코딩 사이트 :http://ostermiller.org/calc/encode.html



    여기도 추천 : http://www.mimul.com/examples/dencoder/


    'Knowledge' 카테고리의 다른 글

    [스크랩] SNMP  (0) 2018.05.25
    [펌] C++로 만든 DLL 을 C#에서 사용하기  (0) 2018.05.24
    [C#][LowCode] Base64 Encoder/Decoder in C#  (0) 2018.05.23
    ASP.NET MVC 3: Razor's @: and <text> syntax  (0) 2018.05.21
    Hashtable foreach  (0) 2018.05.17

    Base64 Encoder/Decoder in C#

    19 Nov 2003
    An implementation of Base64 encoding/decoding in C#

    Introduction

    The code implements the encoding and decoding mechanism of Base64, which is widely used in e-Mail encoding and also .NET Viewstate encoding.

    Background

    It is written based on RFC 1341, using VS.NET 2003.

    Notes

    I didn't implement filtering and inserting carriage return and newline etc.

    Code Listing

    Encoder source

    using System;
    
    namespace TestBase64
    {
      /// <summary>
      /// Summary description for Base64Encoder.
      /// </summary>
      public class Base64Encoder
      {
        byte[] source;
        int length,length2;
        int blockCount;
        int paddingCount;
        public Base64Encoder(byte[] input)
        {
          source=input;
          length=input.Length;
          if((length % 3)==0)
          {
            paddingCount=0;
            blockCount=length/3;
          }
          else
          {
            paddingCount=3-(length % 3);//need to add padding
            blockCount=(length+paddingCount) / 3;
          }
          length2=length+paddingCount;//or blockCount *3
        }
    
        public char[] GetEncoded()
        {
          byte[] source2;
          source2=new byte[length2];
          //copy data over insert padding
          for (int x=0; x<length2;x++)
          {
            if (x<length)
            {
              source2[x]=source[x];
            }
            else
            {
              source2[x]=0;
            }
          }
          
          byte b1, b2, b3;
          byte temp, temp1, temp2, temp3, temp4;
          byte[] buffer=new byte[blockCount*4];
          char[] result=new char[blockCount*4];
          for (int x=0;x<blockCount;x++)
          {
            b1=source2[x*3];
            b2=source2[x*3+1];
            b3=source2[x*3+2];
    
            temp1=(byte)((b1 & 252)>>2);//first
    
            temp=(byte)((b1 & 3)<<4);
            temp2=(byte)((b2 & 240)>>4);
            temp2+=temp; //second
    
            temp=(byte)((b2 & 15)<<2);
            temp3=(byte)((b3 & 192)>>6);
            temp3+=temp; //third
    
            temp4=(byte)(b3 & 63); //fourth
    
            buffer[x*4]=temp1;
            buffer[x*4+1]=temp2;
            buffer[x*4+2]=temp3;
            buffer[x*4+3]=temp4;
    
          }
    
          for (int x=0; x<blockCount*4;x++)
          {
            result[x]=sixbit2char(buffer[x]);
          }
    
          //covert last "A"s to "=", based on paddingCount
          switch (paddingCount)
          {
            case 0:break;
            case 1:result[blockCount*4-1]='=';break;
            case 2:result[blockCount*4-1]='=';
              result[blockCount*4-2]='=';
              break;
            default:break;
          }
          return result;
        }
    
        private char sixbit2char(byte b)
        {
          char[] lookupTable=new char[64]
              {  'A','B','C','D','E','F','G','H','I','J','K','L','M',
                'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
                'a','b','c','d','e','f','g','h','i','j','k','l','m',
                'n','o','p','q','r','s','t','u','v','w','x','y','z',
                '0','1','2','3','4','5','6','7','8','9','+','/'};
    
          if((b>=0) &&(b<=63))
          {
            return lookupTable[(int)b];
          }
          else
          {
            //should not happen;
            return ' ';
          }
        }
      }
    }

    Decoder source

    using System;
    
    namespace TestBase64
    {
      /// <summary>
      /// Summary description for Base64Decoder.
      /// </summary>
      public class Base64Decoder
      {
        char[] source;
        int length, length2, length3;
        int blockCount;
        int paddingCount;
        public Base64Decoder(char[] input)
        {
          int temp=0;
          source=input;
          length=input.Length;
    
          //find how many padding are there
          for (int x=0;x<2;x++)
          {
            if(input[length-x-1]=='=')
              temp++;
          }
          paddingCount=temp;
          //calculate the blockCount;
          //assuming all whitespace and carriage returns/newline were removed.
          blockCount=length/4;
          length2=blockCount*3;
        }
    
        public byte[] GetDecoded()
        {
          byte[] buffer=new byte[length];//first conversion result
          byte[] buffer2=new byte[length2];//decoded array with padding
    
          for(int x=0;x<length;x++)
          {
            buffer[x]=char2sixbit(source[x]);
          }
    
          byte b, b1,b2,b3;
          byte temp1, temp2, temp3, temp4;
    
          for(int x=0;x<blockCount;x++)
          {
            temp1=buffer[x*4];
            temp2=buffer[x*4+1];
            temp3=buffer[x*4+2];
            temp4=buffer[x*4+3];        
    
            b=(byte)(temp1<<2);
            b1=(byte)((temp2 & 48)>>4);
            b1+=b;
    
            b=(byte)((temp2 & 15)<<4);
            b2=(byte)((temp3 & 60)>>2);
            b2+=b;
    
            b=(byte)((temp3 & 3)<<6);
            b3=temp4;
            b3+=b;
    
            buffer2[x*3]=b1;
            buffer2[x*3+1]=b2;
            buffer2[x*3+2]=b3;
          }
          //remove paddings
          length3=length2-paddingCount;
          byte[] result=new byte[length3];
    
          for(int x=0;x<length3;x++)
          {
            result[x]=buffer2[x];
          }
    
          return result;
        }
    
        private byte char2sixbit(char c)
        {
          char[] lookupTable=new char[64]
              {  
    
        'A','B','C','D','E','F','G','H','I','J','K','L','M','N',
        'O','P','Q','R','S','T','U','V','W','X','Y', 'Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m','n',
        'o','p','q','r','s','t','u','v','w','x','y','z',
        '0','1','2','3','4','5','6','7','8','9','+','/'};
          if(c=='=')
            return 0;
          else
          {
            for (int x=0;x<64;x++)
            {
              if (lookupTable[x]==c)
                return (byte)x;
            }
            //should not reach here
            return 0;
          }
    
        }
    
      }
    }


    + Recent posts