728x90

라즈레비파이3 로 무얼 할까 하다가

웹서버를 한번 구축해 볼까 하고 시도해 보았습니다.

이전 포스팅에서 링크가 되어있지만 (http://kjcoder.tistory.com/197)

여러가지 방법이 있으며 제가 시도해서 성공한 내용을 공유해 봅니다.

프로젝트를 생성합니다.

Background 로 돌아가게 할것이기 때문에 아래 처럼 Background Application (IoT) 를 선택합니다. (Visual Studio 2015)

버전은 자신의 라즈베리파이에 맞는 버전에 맞게 설정합니다. (굳이 버전이 안맞아도 최소버전 상위 라면 동작하는 것 같습니다.)

참고로 전 라즈베리파이 버전이 10.0.15063.297 입니다.

이제 위처럼 소스가 만들어 지고 저희가 코딩할 곳은 아래 그림에서 StartupTask.cs 파일입니다.

우선 코딩은 아래와 같습니다. (이해는 나중에....)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text;
using Windows.ApplicationModel.Background;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

// The Background Application template is documented at http://go.microsoft.com/fwlink/?LinkID=533884&clcid=0x409

namespace IoT.BackGroundWebServer
{
    public sealed class StartupTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            //
            // TODO: Insert code to perform background work
            //
            // If you start any asynchronous methods here, prevent the task
            // from closing prematurely by using BackgroundTaskDeferral as
            // described in http://aka.ms/backgroundtaskdeferral
            //

            taskInstance.GetDeferral();

            WebServer server = new WebServer();

            while (server.Start() == true)
            { }
        }
    }

    #region WebServer
    internal class WebServer
    {
        private const uint BufferSize = 8192;

        public bool Start()
        {

            try
            {
                StreamSocketListener listener = new StreamSocketListener();
                listener.BindServiceNameAsync("8081").AsTask();
                listener.ConnectionReceived += async (sender, args) =>
                {
                    StringBuilder request = new StringBuilder();
                    using (Windows.Storage.Streams.IInputStream input = args.Socket.InputStream)
                    {
                        byte[] data = new byte[BufferSize];
                        Windows.Storage.Streams.IBuffer buffer = data.AsBuffer();
                        uint dataRead = BufferSize;
                        while (dataRead == BufferSize)
                        {
                            await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
                            request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
                            dataRead = buffer.Length;
                        }

                        //In the future, maybe we parse the HTTP request and serve different HTML pages for now we just always push index.html
                    }

                    using (IOutputStream output = args.Socket.OutputStream)
                    {
                        using (System.IO.Stream response = output.AsStreamForWrite())
                        {
                            string page = "";
                            var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;

                            // acquire file
                            var file = await folder.GetFileAsync("index.html");
                            var readFile = await Windows.Storage.FileIO.ReadLinesAsync(file);
                            foreach (var line in readFile)
                            {
                                page += line;
                            }

                            byte[] bodyArray = Encoding.UTF8.GetBytes(page);
                            var bodyStream = new MemoryStream(bodyArray);

                            //iCount++;

                            var header = "HTTP/1.1 200 OK\r\n" +
                                        $"Content-Length: {bodyStream.Length}\r\n" +
                                            "Connection: close\r\n\r\n";

                            byte[] headerArray = Encoding.UTF8.GetBytes(header);
                            await response.WriteAsync(headerArray, 0, headerArray.Length);
                            await bodyStream.CopyToAsync(response);
                            await response.FlushAsync();
                        }
                    }
                };

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
    }
    #endregion
}

코딩을 보시면 알겠지만 StreamSocketListener 를 이용하여 웹서버를 만들고 기본 화면으로 index.html 파일을 사용하고 있는걸 알 수 있습니다.

또한 포트를 전 8081포트를 사용하는걸로 설정되어있습니다.

index.html 파일을 만듭니다.

내용은 "webserver test" 라고 간단하게 넣었습니다.

프로젝트 속성에서 자신의 라즈베리파이로 디버깅이 가능하도록 설정합니다.

디버깅 설정 방법은 이전 포스팅을 참고 바랍니다 (http://kjcoder.tistory.com/194)

여기서 중요한게 있습니다.

이상태로 그냥 실행하게 되면 라즈베리파이 화면에서는 표시되지 않지만 에러가 발생됩니다.

+  ex {"Access is denied.\r\n\r\nAt least one of either InternetClientServer or PrivateNetworkClientServer capabilities is required to listen for or receive traffic"} System.Exception {System.UnauthorizedAccessException}

접근이 거부된것을 볼수 있습니다.

이 에러를 해결하기 위해서는 아래처럼 진행해야합니다.

프로젝트 속성창에서 라이브러리 탭의 패키지 매니페스트로 접근합니다.

거기서 기능 탭을 선택하면 아래와 같은 화면이 나오게 됩니다.

여기서 기능을 체크해야하는데 아래 두 곳을 추가로 체크해 줍니다.

서버를 만드는 것이기 때문에 서버 기능을 체크해주어야하는 것입니다.

이제 준비는 되었고 디버깅합니다.

Background 로 돌아가게 했으므로 라즈베리파이 화면에는 아무런 변화가 없습니다.

device portal 에서 아래처럼 현재 동작 되고 있는걸 확인 할수 있습니다.

이제 익스플로러 창에서 확인해 봅니다. 제 라즈베리파이3는 ip 192.168.0.23 입니다. 예제에서 포트는 8081 로 했습니다.

위에서 보듯이 제대로 index.html 이 나타나는걸 확인 할수 있습니다.

iptime 을 쓰고 있는데 공유기 설정하고 해보니 외부에서도 이상없이 접근이 되는걸 확인했습니다.

이제 기능을 추가하여 집안의 기기들을 조작하는걸로 가면 좋을것 같네요.^^

 

아래는 실습한 예제 파일입니다.

IoT.BackGroundWebServer.zip

IoT.BackGroundWebServer.z01

 

728x90
Posted by kjun.kr
,