스트림 소켓을 사용하여 연결하는 방법(HTML)

[ 이 문서는 Windows 런타임 앱을 작성하는 Windows에서 8.x 및 Windows Phone 8.x 개발자를 대상으로 합니다. Windows 10용으로 개발하는 경우에는 최신 설명서를 참조하세요.]

이 항목에서는 Windows 런타임 앱이 StreamSocket을 사용하는 TCP 소켓을 통해 네트워크 데이터를 보내고 받을 수 있도록 하는 방법을 보여 줍니다. TCP 소켓은 수명이 긴 연결에 대해 각 방향으로 하위 수준 네트워크 데이터 전송을 제공합니다. TCP 소켓은 인터넷에서 사용되는 대부분의 네트워크 프로토콜에서 사용하는 기본 기능입니다.

샘플의 클라이언트 구성 요소는 네트워크 연결을 설정하기 위해 TCP 소켓을 만들고, 이 소켓을 사용하여 데이터를 보내고, 소켓을 닫습니다. 샘플의 서버 구성 요소는 네트워크 연결을 수신 대기하고 허용하는 TCP 소켓을 만들고, 수신 소켓에서 연결을 허용한 다음, 소켓을 사용하여 클라이언트에서 데이터를 받고, 소켓을 닫습니다. 이 샘플은 JavaScript, C#, VB 및 C++ 프로그래밍 언어로 제공됩니다.

샘플의 클라이언트 구성 요소는 다음과 같은 기능을 보여 줍니다.

  • StreamSocket 클래스를 사용하여 TCP 소켓을 만듭니다.
  • StreamSocket.ConnectAsync 메서드 중 하나를 사용하여 TCP 네트워크 서버에 대한 네트워크 연결을 만듭니다.
  • 프로그래머가 스트림에 일반적인 유형(정수 및 문자열 등)을 쓸 수 있도록 하는 Streams.DataWriter 개체를 사용하여 서버에 데이터를 보냅니다.
  • 소켓을 닫습니다.

샘플의 서버 구성 요소는 다음과 같은 기능을 보여 줍니다.

참고  이 샘플을 사용하려면 루프백 인터페이스를 사용하여 네트워크에 액세스해야 합니다.

 

사전 요구 사항

다음 예에서는 JavaScript를 사용합니다. 첫 번째 앱을 만드는 자세한 내용은 JavaScript를 사용하여 첫 번째 Windows 스토어 앱 만들기를 참조하세요.

Windows 스토어 앱에서 네트워크에 대비하려면 프로젝트 Package.appxmanifest 파일에서 접근 권한 값을 설정해야 합니다. 각 네트워크 접근 권한 값에 대한 정의는 네트워크 격리 접근 권한 값을 구성하는 방법을 참조하세요.

지침

새 프로젝트 만들기

  1. Microsoft Visual Studio 2013을 열고 파일 메뉴에서 새 프로젝트를 선택합니다.
  2. 템플릿 목록에서 JavaScript를 선택합니다.
  3. 섹션에서 Store apps을 선택합니다.
  4. 섹션에서 대상으로 하는 플랫폼에 따라 Universal Apps, Windows apps 또는 Windows Phone apps을 선택한 후 새 응용 프로그램을 선택합니다.
  5. 응용 프로그램 이름을 socketsSample로 지정하고 확인을 클릭합니다.

네트워크 액세스를 사용하도록 하는 접근 권한 값 설정

앱에 네트워크 액세스가 필요한 경우 앱에 대해 네트워크 접근 권한 값을 설정해야 합니다. StreamSocket을 사용하여 네트워크 서비스에 연결하는 앱은 네트워크 접근 권한 값을 설정해야 합니다.

앱이 인터넷의 원격 서비스에 클라이언트로 연결할 수 있으려면 인터넷(클라이언트) 접근 권한 값이 필요합니다. 앱이 홈 네트워크 또는 회사 네트워크의 원격 서비스에 클라이언트로 연결할 수 있어야 하는 경우 개인 네트워크(클라이언트 및 서버) 접근 권한 값이 필요합니다.

앱이 StreamSocketListener를 사용하여 인터넷의 원격 끝점에서 수신 연결을 수신 대기해야 하는 경우 인터넷(클라이언트 및 서버) 접근 권한 값이 필요합니다. 앱이 StreamSocketListener를 사용하여 홈 네트워크 또는 회사 네트워크의 원격 끝점에서 수신 연결을 수신 대기해야 하는 경우 개인 네트워크(클라이언트 및 서버) 접근 권한 값이 필요합니다.

참고  Windows Phone에는 앱에서 네트워크에 완전히 액세스하게 해 주는 단일 네트워크 접근 권한 값(인터넷(클라이언트 및 서버))이 있습니다.

 

수신 연결을 수신 대기하는 이 샘플의 서버 구성 요소가 클라이언트 구성 요소와 동일한 장치에서 실행되고 있는 경우 루프백 액세스가 필요합니다. Visual Studio 2013에서 개발되어 실행되는 앱은 루프백 제한이 면제되도록 자동으로 등록됩니다. 자세한 내용은 루프백을 사용하도록 설정하고 네트워크 격리를 디버그하는 방법을 참조하세요.

네트워크 액세스에 대한 자세한 내용은 네트워크 격리 접근 권한 값을 구성하는 방법을 참조하세요.

이 단계는 앱이 인터넷이나 홈 또는 회사 네트워크의 네트워크 서비스에 액세스하는 경우 배포되기 전에 앱의 네트워크 접근 권한 값을 설정하는 데 필요합니다.

  1. Microsoft Visual Studio를 사용하여 package.appxmanifest 파일을 엽니다.

  2. 접근 권한 값 탭을 선택합니다.

  3. Windows 버전 샘플을 빌드하려면 인터넷(클라이언트)개인 네트워크(클라이언트 및 서버) 접근 권한 값을 선택합니다.

    Windows Phone 버전 샘플을 빌드하려면 인터넷(클라이언트 및 서버) 접근 권한 값을 선택합니다.

  4. 매니페스트 파일을 저장하고 닫습니다.

HTML UI 추가

  1. html 폴더를 엽니다. 새 startListener.html 파일을 열고 <head> 및 <body> 섹션에 다음 HTML을 추가합니다.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/socketsSample.js"></script>
        <script src="/js/startListener.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                StreamSocketListener will create the "server" side of a connection. It listens on
                a "service name" (often a port number) and calls a callback when it accepts a connection;
                this happens when some other application tries to connect. Once a connection is
                accepted, the acceptAsync() method needs to be called again.
            </p>
            <p>
                <label for="serviceNameAccept">Service Name:</label>
                <input id="serviceNameAccept" type="text" />
            </p>
            <p>
                <button id="buttonStartListener">Create StreamSocketListener and start to listen</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  2. html 폴더를 엽니다. 새 connectToListener.html 파일을 열고 <head> 및 <body> 섹션에 다음 HTML을 추가합니다.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/connectToListener.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                Next, you need the "other side of the connection" -- you need to connect to a listener.
                The host name and service name (often a port number) to connect to are the "Host
                Name:" and "Service Name:" entries. The service name should match what you started
                to listen to!
            </p>
            <p>
                The connection will automatically use IPv6 as needed. It will also resolve international
                domain names.
            </p>
            <p>
                Due to the network security system, you cannot connect to other applications running
                on the same machine. This means that you can only use "localhost" to connect to
                the same application (specifically, you can connect to a listener on the same machine
                running in the same app container)
            </p>
            <p>
                <label for="hostNameConnect">Host Name:</label>
                <input id="hostNameConnect" type="text" />
            </p>
            <p>
                <label for="serviceNameConnect">Service Name:</label>
                <input id="serviceNameConnect" type="text" />
            </p>
            <p>
                <button id="buttonOpen">Connect Now</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  3. html 폴더를 엽니다. 새 sendData.html 파일을 열고 <head> 및 <body> 섹션에 다음 HTML을 추가합니다.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/sendData.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>
                Now you can send data to the "server". Sending data is often done with the DataWriter
                object; it will write to the socket stream. You can also hook up the socket stream
                to other streams in Windows 8.
            </p>
            <p>
                <button id="buttonSend">Send 'hello' now</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    
  4. html 폴더를 엽니다. 새 closeSocket.html 파일을 열고 <head> 및 <body> 섹션에 다음 HTML을 추가합니다.

    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <script src="/js/closeSocket.js"></script>
    </head>
    <body>
        <div data-win-control="SdkSample.ScenarioInput">
            <p>Lastly, you can close all sockets.</p>
            <p>If you don't close your socket, it will be closed for you when the application exits.</p>
            <p>
                <button id="buttonClose">Close all sockets</button>
            </p>
        </div>
        <div data-win-control="SdkSample.ScenarioOutput">
            <p id="statusBox"></p>
            <p id="outputBox"></p>
        </div>
    </body>
    </html>
    

샘플 및 시나리오 정의

이 단계의 코드는 샘플, HTML 파일 및 샘플에서 사용하는 시나리오를 정의합니다. 이 코드는 또한 이벤트 수신기를 추가하고 앱을 시작합니다. 시나리오 옵션을 통해 사용자는 소켓 수신기를 시작하고, 클라이언트를 시작하여 수신기에 연결하고, 클라이언트가 일부 데이터를 서버로 보내도록 하고, 소켓을 닫을 수 있습니다.

  • js 폴더를 엽니다. default.js 파일을 열고 파일에 다음 코드를 추가합니다.

        var sampleTitle = "StreamSocket";
    
        var scenarios = [
            { url: "/html/startListener.html", title: "Start StreamSocketListener" },
            { url: "/html/connectToListener.html", title: "Connect to Listener" },
            { url: "/html/sendData.html", title: "Send Data" },
            { url: "/html/closeSocket.html", title: "Close Socket" }
        ];
    
        function activated(eventObject) {
            if (eventObject.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) {
                // Use setPromise to indicate to the system that the splash screen must not be torn down
                // until after processAll and navigate complete asynchronously.
                eventObject.setPromise(WinJS.UI.processAll().then(function () {
                    // Navigate to either the first scenario or to the last running scenario
                    // before suspension or termination.
                    var url = WinJS.Application.sessionState.lastUrl || scenarios[0].url;
                    return WinJS.Navigation.navigate(url);
                }));
            }
        }
    
        WinJS.Navigation.addEventListener("navigated", function (eventObject) {
            var url = eventObject.detail.location;
            var host = document.getElementById("contentHost");
            // Call unload method on current scenario, if there is one
            host.winControl && host.winControl.unload && host.winControl.unload();
            WinJS.Utilities.empty(host);
            eventObject.detail.setPromise(WinJS.UI.Pages.render(url, host, eventObject.detail.state).then(function () {
                WinJS.Application.sessionState.lastUrl = url;
            }));
        });
    
        WinJS.Namespace.define("SdkSample", {
            sampleTitle: sampleTitle,
            scenarios: scenarios
        });
    
        WinJS.Application.addEventListener("activated", activated, false);
        WinJS.Application.start();
    

소켓 및 이벤트 함수에 대한 변수 정의

이 단계의 코드는 수신기 소켓, 클라이언트 소켓, 서버 읽기 소켓을 비롯한 다양한 변수와 오류 및 이벤트에 대한 다양한 변수를 만듭니다. 변수는 클라이언트 소켓이 연결된 상태 또는 닫힌 상태인지 여부를 추적하기 위해 만듭니다. 이 단계에서는 또한 서버에서 연결할 호스트 이름과 서비스 이름(TCP 포트)을 정의합니다. 호스트 이름 및 서비스 이름의 값은 UI에서 변경할 수 있는 기본값으로 설정됩니다.

  • js 폴더를 엽니다. 새 socketsSample.js 파일을 열고 파일에 다음 코드를 추가합니다.

    var socketsSample = {};
    
    (function () {
        "use strict";
    
        socketsSample.listener = null; // A StreamSocketListener that acts as our server.
        socketsSample.serverSocket = null; // The server socket that's been accepted.
        socketsSample.serverReader = null; // The reader for the server socket.
        socketsSample.clientSocket = null; // The client socket that will connect to the server socket.
        socketsSample.connected = false;
        socketsSample.closing = false;
    
        socketsSample.serviceNameAccept = "22112";
        socketsSample.hostNameConnect = "localhost";
        socketsSample.serviceNameConnect = "22112";
    
        socketsSample.displayStatus = function (message) {
            document.getElementById("statusBox").innerHTML = message;
        };
    
        socketsSample.displayOutput = function (message) {
            document.getElementById("outputBox").innerHTML = message;
        };
    
        socketsSample.setValues = function () {
            var serviceNameAcceptInput = document.getElementById("serviceNameAccept");
            var hostNameConnectInput = document.getElementById("hostNameConnect");
            var serviceNameConnectInput = document.getElementById("serviceNameConnect");
    
            if (serviceNameAcceptInput) {
                serviceNameAcceptInput.value = socketsSample.serviceNameAccept;
            }
            if (hostNameConnectInput) {
                hostNameConnectInput.value = socketsSample.hostNameConnect;
            }
            if (serviceNameConnectInput) {
                serviceNameConnectInput.value = socketsSample.serviceNameConnect;
            }
        };
    
        socketsSample.getValues = function (evt) {
            switch (evt.target.id) {
                case "serviceNameAccept":
                    socketsSample.serviceNameAccept = evt.target.value;
                    break;
                case "hostNameConnect":
                    socketsSample.hostNameConnect = evt.target.value;
                    break;
                case "serviceNameConnect":
                    socketsSample.serviceNameConnect = evt.target.value;
                    break;
            }
        };
    })();
    

수신기 만들기 및 서비스 이름(포트) 수신 대기 시작

이 섹션의 코드는 수신기를 만들고 수신 대기를 시작합니다. 수신기가 IP 주소 및 TCP 포트에 바인딩되고 연결을 수락하고 클라이언트에서 보낸 데이터를 읽도록 사용자가 요청할 경우 이벤트를 처리하는 함수도 추가됩니다.

참고  이 특정 예제가 독립적(클라이언트 및 서버가 동일한 앱에 있음)일지라도 일반적으로는 클라이언트 앱과 서버 앱을 별도로 갖게 됩니다.

 

  • js 폴더를 엽니다. 새 startListener.js 파일을 열고 파일에 다음 코드를 추가합니다.

        var page = WinJS.UI.Pages.define("/html/startListener.html", {
            ready: function (element, options) {
                document.getElementById("buttonStartListener").addEventListener("click", startListener, false);
                document.getElementById("serviceNameAccept").addEventListener("change", socketsSample.getValues, false);
                socketsSample.setValues();
            }
        });
    
        function startListener() {
            if (socketsSample.listener) {
                socketsSample.displayStatus("Already have a listener; call close to close the listener.");
                return;
            }
            socketsSample.closing = false;
            var serviceName = document.getElementById("serviceNameAccept").value;
            socketsSample.listener = new Windows.Networking.Sockets.StreamSocketListener(serviceName);
            socketsSample.listener.addEventListener("connectionreceived", onServerAccept);
            socketsSample.displayStatus("Server: listener creation started.");
            socketsSample.listener.bindServiceNameAsync(serviceName).done(function () {
                socketsSample.displayStatus("Server: listener creation completed.");
            }, onError);
        }
    
        // This has to be a real function ; it will "loop" back on itself with the
        // call to acceptAsync at the very end.
        function onServerAccept(eventArgument) {
            socketsSample.displayStatus("Server: connection accepted.");
            socketsSample.serverSocket = eventArgument.socket;
            socketsSample.serverReader = new Windows.Storage.Streams.DataReader(socketsSample.serverSocket.inputStream);
            startServerRead();
        }
    
        // The protocol here is simple: a four-byte 'network byte order' (big-endian) integer
        // that says how long a string is, and then a string that is that long.
        // We wait for exactly 4 bytes, read in the count value, and then wait for
        // count bytes, and then display them.
        function startServerRead() {
            socketsSample.serverReader.loadAsync(4).done(function (sizeBytesRead) {
                // Make sure 4 bytes were read.
                if (sizeBytesRead !== 4) {
                    socketsSample.displayStatus("Server: connection lost.");
                    return;
                }
    
                // Read in the 4 bytes count and then read in that many bytes.
                var count = socketsSample.serverReader.readInt32();
                return socketsSample.serverReader.loadAsync(count).then(function (stringBytesRead) {
                    // Make sure the whole string was read.
                    if (stringBytesRead !== count) {
                        socketsSample.displayStatus("Server: connection lost.");
                        return;
                    }
                    // Read in the string.
                    var string = socketsSample.serverReader.readString(count);
                    socketsSample.displayOutput("Server read: " + string);
                    // Restart the read for more bytes.
                    startServerRead();
                }); // End of "read in rest of string" function.
            }, onError);
        }
    
        function onError(reason) {
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

소켓 만들기 및 원격 끝점에 연결

이 단계의 코드는 StreamSocket.ConnectAsync 메서드를 사용하여 소켓을 만들고 원격 끝점(일반적으로 서버)에 연결하는 함수를 추가합니다. 클라이언트가 연결하려고 할 때 오류가 있는 경우를 처리하기 위한 함수도 추가됩니다.

  • js 폴더를 엽니다. 새 connectToListener.js 파일을 열고 파일에 다음 코드를 추가합니다.

        var page = WinJS.UI.Pages.define("/html/connectToListener.html", {
            ready: function (element, options) {
                document.getElementById("buttonOpen").addEventListener("click", openClient, false);
                document.getElementById("hostNameConnect").addEventListener("change", socketsSample.getValues, false);
                document.getElementById("serviceNameConnect").addEventListener("change", socketsSample.getValues, false);
                socketsSample.setValues();
            }
        });
    
        function openClient() {
            if (socketsSample.clientSocket) {
                socketsSample.displayStatus("Already have a client; call close to close the listener and the client.");
                return;
            }
            socketsSample.closing = false;
            var serverHostName = new Windows.Networking.HostName(document.getElementById("hostNameConnect").value);
            var serviceName = document.getElementById("serviceNameConnect").value;
            socketsSample.clientSocket = new Windows.Networking.Sockets.StreamSocket();
            socketsSample.displayStatus("Client: connection started.");
            socketsSample.clientSocket.connectAsync(serverHostName, serviceName).done(function () {
                socketsSample.displayStatus("Client: connection completed.");
                socketsSample.connected = true;
            }, onError);
        }
    
        function onError(reason) {
            socketsSample.clientSocket = null;
    
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

클라이언트에서 데이터 보내기 및 받기

이 단계의 코드는 Windows.Storage.Stream.DataWriter 클래스의 메서드를 사용하여 데이터를 서버로 보내는 함수를 추가합니다.

  • js 폴더를 엽니다. 새 sendData.js 파일을 열고 파일에 다음 코드를 추가합니다.

        var page = WinJS.UI.Pages.define("/html/sendData.html", {
            ready: function (element, options) {
                document.getElementById("buttonSend").addEventListener("click", sendHello, false);
            }
        });
    
        function sendHello() {
            if (!socketsSample.connected) {
                socketsSample.displayStatus("Client: you must connect the client before using it.");
                return;
            }
            var writer = new Windows.Storage.Streams.DataWriter(socketsSample.clientSocket.outputStream);
            var string = "Hello World";
            var len = writer.measureString(string); // Gets the UTF-8 string length.
            writer.writeInt32(len);
            writer.writeString(string);
            socketsSample.displayStatus("Client sending: " + string + ".");
            writer.storeAsync().done(function () {
                socketsSample.displayStatus("Client sent: " + string + ".");
                writer.detachStream();
            }, onError);
        }
    
        function onError(reason) {
            // When we close a socket, outstanding async operations will be canceled and the
            // error callbacks called.  There's no point in displaying those errors.
            if (!socketsSample.closing) {
                socketsSample.displayStatus(reason);
            }
        }
    

소켓 닫기

이 단계의 코드는 StreamSocket.Close 메서드를 사용하여 소켓을 닫습니다. 소켓이 닫히면 모든 보류 중인 작업이 종료되고 오류 루틴이 호출됩니다.

  • js 폴더를 엽니다. 새 socketClose.js 파일을 열고 파일에 다음 코드를 추가합니다.

        var page = WinJS.UI.Pages.define("/html/closeSocket.html", {
            ready: function (element, options) {
                document.getElementById("buttonClose").addEventListener("click", closeListenerAndSockets, false);
            }
        });
    
        function closeListenerAndSockets() {
            socketsSample.closing = true;
            if (socketsSample.listener) {
                socketsSample.listener.close();
                socketsSample.listener = null;
            }
            if (socketsSample.serverSocket) {
                socketsSample.serverSocket.close();
                socketsSample.serverSocket = null;
            }
            if (socketsSample.clientSocket) {
                socketsSample.clientSocket.close();
                socketsSample.clientSocket = null;
                socketsSample.connected = false;
            }
            socketsSample.displayStatus("Client and server closed.");
        }
    

응용 프로그램 실행

  • 앱을 실행하려면 Visual Studio에서 F5 키를 눌러 프로젝트를 실행합니다. 수신기를 시작하고, 클라이언트를 수신기에 연결하고, 데이터를 보내고, 소켓을 닫으려면 해당 단추를 선택합니다.

요약 및 다음 단계

이 항목에서는 TCP 스트림 소켓을 사용하여 네트워크에 연결하고 StreamSocket 개체를 사용하여 데이터를 보내는 앱을 만들었습니다. 앱은 또한 TCP 연결을 수신 대기하고 데이터를 보내거나 받는 데 사용할 수 있는 스트림 소켓의 연결을 수락하는 방법을 보여 주었습니다.

이 항목의 소스 코드 및 빌드 파일은 StreamSocket 샘플로 제공됩니다.

데이터그램 소켓을 사용하여 데이터를 보내는 데 사용되는 네트워크 연결을 설정할 수도 있습니다. 이에 대한 예는 데이터그램 소켓을 사용하여 연결하는 방법을 참조하세요.

관련 항목

다른 리소스

소켓을 사용하여 연결

네트워크 접근 권한 값을 구성하는 방법

데이터그램 소켓을 사용하여 연결하는 방법

루프백을 사용하도록 설정하고 네트워크 격리를 디버깅하는 방법

TLS/SSL을 통해 소켓 연결의 보안을 유지하는 방법

소켓 작업에 시간 제한을 설정하는 방법

고급 소켓 컨트롤을 사용하는 방법

네트워크 연결 문제 해결 및 디버그

참조

StreamSocket

StreamSocketListener

Windows.Networking

Windows.Networking.Sockets

Windows.Storage.Stream.DataWriter

샘플

StreamSocket 샘플