frostar@wiki
WebSocketサーバ
最終更新:
frostar
-
view
ちょっとJavaScriptとやりとりするサーバを作りたいと思ったので
winsockを使ってWebSocketサーバを作成しました。
とりあえず以下は単純なechoサーバです。
クライアント側との接続を確立するためにメッセージを解析して
それにSHA1とBase64という処理をかけたものを返す必要があるのですが、
その2つに関しては省略しています(探せば見つかると思います)。
クライアント側はEchoTestなどを使って試してみてください。
長すぎる文字列には対応してません(124文字くらいまで)。
winsockを使ってWebSocketサーバを作成しました。
とりあえず以下は単純なechoサーバです。
クライアント側との接続を確立するためにメッセージを解析して
それにSHA1とBase64という処理をかけたものを返す必要があるのですが、
その2つに関しては省略しています(探せば見つかると思います)。
クライアント側はEchoTestなどを使って試してみてください。
長すぎる文字列には対応してません(124文字くらいまで)。
- #include <stdio.h>
- #include <winsock2.h>
-
- void SHA1(char* pszKey,int key_len,char* hash);//SHA1を返す
- void Base64(char* hash,int hash_len,char* sz);//Base64を返す
-
- bool HandShake(SOCKET sock);
-
- int main(){
- WSADATA wsaData;
- SOCKET sock0;
- struct sockaddr_in addr;
- struct sockaddr_in client;
- SOCKET sock;
- int len;
- BOOL yes = 1;
-
- char inbuf[4096];
-
- if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0) {
- return 1;
- }
-
- sock0 = socket(AF_INET, SOCK_STREAM, 0);
- if(sock0 == INVALID_SOCKET) {
- printf("socket : %d\n", WSAGetLastError());
- return 1;
- }
-
- addr.sin_family = AF_INET;
- addr.sin_port = htons(12345);
- addr.sin_addr.S_un.S_addr = INADDR_ANY;
- setsockopt(sock0,SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
- if (bind(sock0, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
- printf("bind : %d\n", WSAGetLastError());
- return 1;
- }
- if (listen(sock0, 5) != 0) {
- printf("listen : %d\n", WSAGetLastError());
- return 1;
- }
- bool isHandShaked = false;
- while (1) {
- len = sizeof(client);
- sock = accept(sock0, (struct sockaddr *)&client, &len);
- if (sock == INVALID_SOCKET) {
- printf("accept : %d\n", WSAGetLastError());
- }
- //ハンドシェイクを行う
- isHandShaked = HandShake(sock);
- if(isHandShaked){
- while(1){
- memset(inbuf, 0, sizeof(inbuf));
- int len = recv(sock, inbuf, sizeof(inbuf), 0);
- if(len<=0)break;
- //マスクを外す
- inbuf[1] = inbuf[1]&0x7f;
- char mask[4];
- for(int i=0;i<4;i++){
- mask[i] = inbuf[2+i];
- }
- for(int i=6;i<len;i++){
- inbuf[i-4] = inbuf[i]^mask[(i-6)%4];
- }
- inbuf[len-4] = '\0';
- //echo
- send(sock,inbuf,strlen(inbuf),0);
- }
- printf("SocketClosed\n");
- }
- closesocket(sock);
- }
- WSACleanup();
- return 0;
- }
-
- bool HandShake(SOCKET sock){
- char inbuf[1024];
- memset(inbuf, 0, sizeof(inbuf));
- //クライアントハンドシェイクを取得
- if(recv(sock, inbuf, sizeof(inbuf), 0)<=0)return false;
- printf("ClientHandShake:\n%s",inbuf);
- //ハンドシェイクを分割してSec-WebSocket-Keyの値を取得
- char *delim = " \r\n";
- char *ctx;
- char *next = strtok_s(inbuf,delim,&ctx);
- char * pszKeyBase;
- while(next){
- next = strtok_s(NULL, delim, &ctx);
- if(strcmp(next,"Sec-WebSocket-Key:")==0){
- pszKeyBase = strtok_s(NULL, delim, &ctx);
- break;
- }
- }
- char pszKey[1024];
- memset(pszKey, 0, sizeof(pszKey));
- sprintf_s(pszKey,sizeof(pszKey), "%s%s",
- pszKeyBase,"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
- //SHA1
- char hash[20];
- SHA1(pszKey,strlen(pszKey),hash);
- char pszAcceptKey[256];
- //BASE64
- Base64(hash,20,pszAcceptKey);
- //サーバハンドシェイク作成
- char pszHeader[1024];
- memset(pszHeader, 0, sizeof(pszHeader));
- sprintf_s(pszHeader, sizeof(pszHeader),
- "HTTP/1.1 101 Switching Protocols\r\n"
- "Upgrade: websocket\r\n"
- "Connection: Upgrade\r\n"
- "Sec-WebSocket-Accept: %s\r\n\r\n",pszAcceptKey);
- printf("ServerHandShake:\n%s",pszHeader);
- //サーバハンドシェイク送信
- send(sock, pszHeader, (int)strlen(pszHeader), 0);
- return true;
- }
-
ハンドシェイク時に112行目のように"\r\n\r\n"と最後に2つ改行をつける必要があるところに注意してください。
これが1つだけだとうまく接続が確立できません。多すぎてもダメです。
また、接続確立後にechoするときは受け取ったフレームを解析してその中のマスク情報を使って
マスクを外してからフレームを送り返す必要があります(ただし、FireFoxだとマスクがかかったままでも行けました)。
フレームの仕様などについてはRFC6455を参照してください。
これが1つだけだとうまく接続が確立できません。多すぎてもダメです。
また、接続確立後にechoするときは受け取ったフレームを解析してその中のマスク情報を使って
マスクを外してからフレームを送り返す必要があります(ただし、FireFoxだとマスクがかかったままでも行けました)。
フレームの仕様などについてはRFC6455を参照してください。