HTML5 Websocket 连接到 Windows .NET 服务器,但没有收到 WebSocket.onopen 通知。

Posted

技术标签:

【中文标题】HTML5 Websocket 连接到 Windows .NET 服务器,但没有收到 WebSocket.onopen 通知。【英文标题】:HTML5 Websocket connects to Windows .NET server but does not recieve the WebSocket.onopen notification.. 【发布时间】:2012-05-09 04:27:24 【问题描述】:

下面是简单的服务器代码

// Webserver1.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "Webserver1.h"
#ifndef UNICODE
#define UNICODE
#endif

#define WIN32_LEAN_AND_MEAN

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>

// Need to link with Ws2_32.lib
#pragma comment(lib, "ws2_32.lib")

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
TCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)

    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_WEBSERVER1, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    
        return FALSE;
    

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WEBSERVER1));

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        
    

    return (int) msg.wParam;




//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)

    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WEBSERVER1));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_WEBSERVER1);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);


//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   
      return FALSE;
   

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;


//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//

int ServerThread(LPVOID param)

    //----------------------
    // Initialize Winsock.
    WSADATA wsaData;
    int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != NO_ERROR) 
        wprintf(L"WSAStartup failed with error: %ld\n", iResult);
        return 1;
    
    //----------------------
    // Create a SOCKET for listening for
    // incoming connection requests.
    SOCKET ListenSocket;
    ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == INVALID_SOCKET) 
        wprintf(L"socket failed with error: %ld\n", WSAGetLastError());
        WSACleanup();
        return 1;
    
    //----------------------
    // The sockaddr_in structure specifies the address family,
    // IP address, and port for the socket that is being bound.
    sockaddr_in service;
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr("127.0.0.1");
    service.sin_port = htons(27015);

    if (bind(ListenSocket,
             (SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR) 
        wprintf(L"bind failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    
    //----------------------
    // Listen for incoming connection requests.
    // on the created socket
    if (listen(ListenSocket, 1) == SOCKET_ERROR) 
        wprintf(L"listen failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    
    //----------------------
    // Create a SOCKET for accepting incoming requests.
    SOCKET AcceptSocket;
    wprintf(L"Waiting for client to connect...\n");

    //----------------------
    // Accept the connection.
    AcceptSocket = accept(ListenSocket, NULL, NULL);
    if (AcceptSocket == INVALID_SOCKET) 
        wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
     else
        wprintf(L"Client connected.\n");

    char *sendbuf = "Client: sending data test";
    char recvbuff[1024];
    int dwBytesWrite;
/*

        dwBytesWrite = recv(AcceptSocket,recvbuff , (int)sizeof(recvbuff), 0);
       if (SOCKET_ERROR == dwBytesWrite )
       
            printf("rev failed with error: %d\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return -1;

       
*/
        dwBytesWrite = send(AcceptSocket,sendbuf , (int)strlen(sendbuf), 0);
       if (SOCKET_ERROR == dwBytesWrite )
       
            printf("rev failed with error: %d\n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return -1;

       
    // No longer need server socket
    closesocket(ListenSocket);

    WSACleanup();
    return 0;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    DWORD dwThreadId;
    HANDLE dispthread;

    switch (message)
    
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case ID_FILE_STARTSERVER:
            dispthread=CreateThread(NULL,0,LPTHREAD_START_ROUTINE(ServerThread),NULL,0,&dwThreadId);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO: Add any drawing code here...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    
    return 0;


// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        
        break;
    
    return (INT_PTR)FALSE;

这是 html5 客户端代码

<!DOCTYPE html>
<html>
<script type="text/javascript">
function WebSocketTest()

  if ("WebSocket" in window)
  
     alert("WebSocket is supported by your Browser!");
     // Let us open a web socket
     var ws = new WebSocket("ws://127.0.0.1:27015/echo");
     ws.onopen = function(evt)
     
     alert("Message i11111");
        // Web Socket is connected, send data using send()
        ws.send("Message to send");
        alert("Message is sent...");
     ;
     ws.onmessage = function (evt) 
      
        var received_msg = evt.data;
        alert("Message is received...");
     ;
     ws.onerror = function (evt) 
      
      //  var received_msg = evt.data;
        alert("ERROR...!!");
     ;
     ws.onclose = function()
      
        // websocket is closed.
        alert("Connection is closed..."); 
     ;
  
  else
  
     // The browser doesn't support WebSocket
     alert("WebSocket NOT supported by your Browser!");
  

</script>
<head>
</head>
<body>
<div id="sse">
   <a href="javascript:WebSocketTest()">Run WebSocket</a>
</div>
<canvas id="myCanvas"   style="border:3px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
</body>
</html>

在调试时我发现客户端连接被服务器接受,但客户端没有收到 onopen 通知,但收到了 onclose 通知,可能是什么问题?

【问题讨论】:

【参考方案1】:

客户端的WebSocket只有在双方进行握手并成功时才会抛出onopen事件。 当 WebSocket 连接到服务器时,它会发送包含一些详细信息的 HTTP 开放握手行。它包含重要信息,服务器应根据来自客户端的数据生成 HTTP 开放握手答案。 尤其是来自客户端的密钥,必须进行处理并生成答案密钥。 握手完成并通过验证后,“onopen”事件将在客户端触发,客户端可以在此之后发送消息,服务器也是如此。否则在一些超时之后客户端会抛出 onerror 和 onclose 并且浏览器会强制断开套接字。

查看官方规范RFC 6455, Section 1.3

【讨论】:

我对此很陌生,您能否详细说明或提供一个示例来说明它是如何完成的...... 这是我前段时间的一篇大文章,里面有一些做握手等的代码部分。 ***.com/questions/10200910/… 你也可以在这里找到一些有用的东西:github.com/dude-seriously/gh12-server 在 Protocol 和 Sockets 文件夹下。

以上是关于HTML5 Websocket 连接到 Windows .NET 服务器,但没有收到 WebSocket.onopen 通知。的主要内容,如果未能解决你的问题,请参考以下文章

HTML5 Websocket 连接到 Windows .NET 服务器,但没有收到 WebSocket.onopen 通知。

从浏览器请求连接到 C# 程序后,WebSocket 没有响应

WebSocket 不会连接到 127.0.0.1 / localhost 以外的任何东西

多个 HTML5 WebSocket 连接

适用于 Android 的 phonegap 中的 WebSocket HTML5

HTML5 Websocket 在发送消息之前等待连接和就绪状态更改