From b6c8cbba8dc3c48fee9674df04073b6e8082eae1 Mon Sep 17 00:00:00 2001 From: Bob-FU Date: Sun, 28 Mar 2021 22:44:51 +0900 Subject: [PATCH 1/8] =?UTF-8?q?JSONWebSocket=E4=BB=95=E6=A7=98=E3=81=AE?= =?UTF-8?q?=E6=96=B0=E3=83=8B=E3=82=B3=E5=AE=9F=E6=B3=81=E3=81=AB=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NiconicoUtils/NicoLiveCommentReceiver.cs | 70 ++++++++++------- .../NiconicoCommentJsonParser.cs | 77 +++++++++++++++++++ TVTComment/TVTComment.csproj | 1 + 3 files changed, 121 insertions(+), 27 deletions(-) create mode 100644 TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs diff --git a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs index 3a7bdd9..6e57e4d 100644 --- a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs +++ b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs @@ -3,6 +3,7 @@ using System.IO; using System.Net.Http; using System.Net.Sockets; +using System.Net.WebSockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; @@ -96,26 +97,23 @@ public async IAsyncEnumerable Receive(string liveId, [Enu throw new InvalidPlayerStatusNicoLiveCommentReceiverException("現在放送されていないか、コミュニティ限定配信のためコメント取得できませんでした"); var threadId = playerStatusRoot.GetProperty("data").GetProperty("rooms")[0].GetProperty("threadId").GetString(); - var msUriStr = playerStatusRoot.GetProperty("data").GetProperty("rooms")[0].GetProperty("xmlSocketUri").GetString(); + var msUriStr = playerStatusRoot.GetProperty("data").GetProperty("rooms")[0].GetProperty("webSocketUri").GetString(); if (threadId == null || msUriStr == null) { throw new InvalidPlayerStatusNicoLiveCommentReceiverException(str.ToString()); } - var msUri = new Uri(msUriStr); - using var tcpClinet = new TcpClient(msUri.Host, msUri.Port); - var socketStream = tcpClinet.GetStream(); - using var socketReader = new StreamReader(socketStream, Encoding.UTF8); + ClientWebSocket ws = new ClientWebSocket(); + var uri = new Uri(msUriStr); - using var __ = cancellationToken.Register(() => - { - socketReader.Dispose(); // socketReader.ReadAsyncを強制終了 - }); + //サーバに対し、接続を開始 + await ws.ConnectAsync(uri, cancellationToken); + var buffer = new byte[1024]; - string body = $"\0"; + string body= "[{\"ping\":{\"content\":\"rs:0\"}},{\"ping\":{\"content\":\"ps:0\"}},{\"thread\":{\"thread\":\"" + threadId + "\",\"version\":\"20061206\",\"user_id\":\"guest\",\"res_from\":-10,\"with_global\":1,\"scores\":1,\"nicoru\":0}},{\"ping\":{\"content\":\"pf:0\"}},{\"ping\":{\"content\":\"rf:0\"}}]"; byte[] bodyEncoded = Encoding.UTF8.GetBytes(body); try { - await socketStream.WriteAsync(bodyEncoded, 0, bodyEncoded.Length, cancellationToken).ConfigureAwait(false); + await ws.SendAsync(bodyEncoded, WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false); } catch (Exception e) when (e is ObjectDisposedException || e is SocketException || e is IOException) { @@ -127,28 +125,46 @@ public async IAsyncEnumerable Receive(string liveId, [Enu throw new NetworkNicoLiveCommentReceiverException(e); } - //コメント受信ループ + //情報取得待ちループ while (true) { - char[] buf = new char[2048]; - int receivedByte; - try + var segment = new ArraySegment(buffer); + var result = await ws.ReceiveAsync(segment, cancellationToken); + + //エンドポイントCloseの場合、処理を中断 + if (result.MessageType == WebSocketMessageType.Close) + { + await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "OK", + cancellationToken); + break; + } + + //バイナリの場合は、当処理では扱えないため、処理を中断 + if (result.MessageType == WebSocketMessageType.Binary) { - receivedByte = await socketReader.ReadAsync(buf, 0, buf.Length).ConfigureAwait(false); + await ws.CloseAsync(WebSocketCloseStatus.InvalidMessageType, + "Binary not supported.", cancellationToken); + break; } - catch (Exception e) when (e is ObjectDisposedException || e is SocketException || e is IOException) + + int count = result.Count; + while (!result.EndOfMessage) { - if (cancellationToken.IsCancellationRequested) - throw new OperationCanceledException(null, e, cancellationToken); - if (e is ObjectDisposedException) - throw; - else - throw new NetworkNicoLiveCommentReceiverException(e); + if (count >= buffer.Length) + { + await ws.CloseAsync(WebSocketCloseStatus.InvalidPayloadData, + "That's too long", CancellationToken.None); + throw new ConnectionClosedNicoLiveCommentReceiverException(); + } + segment = new ArraySegment(buffer, count, buffer.Length - count); + result = await ws.ReceiveAsync(segment, cancellationToken); + + count += result.Count; } - if (receivedByte == 0) - break; // 4時リセットかもしれない→もう一度試す - this.parser.Push(new string(buf[..receivedByte])); + //メッセージを取得 + var message = Encoding.UTF8.GetString(buffer, 0, count); + this.parser.Push(message); while (this.parser.DataAvailable()) yield return this.parser.Pop(); } @@ -162,6 +178,6 @@ public void Dispose() } private readonly HttpClient httpClient; - private readonly NiconicoCommentXmlParser parser = new NiconicoCommentXmlParser(true); + private readonly NiconicoCommentJsonParser parser = new NiconicoCommentJsonParser(true); } } \ No newline at end of file diff --git a/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs b/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs new file mode 100644 index 0000000..de39872 --- /dev/null +++ b/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs @@ -0,0 +1,77 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; + +namespace TVTComment.Model.NiconicoUtils +{ + class NiconicoCommentJsonParser + { + private bool socketFormat; + private Queue chats = new Queue(); + private string buffer; + + /// + /// を初期化する + /// + /// ソケットを使うリアルタイムのデータ形式ならtrue 過去ログなどのデータ形式ならfalse + public NiconicoCommentJsonParser(bool socketFormat) + { + this.socketFormat = socketFormat; + } + + public void Push(string str) + { + if (socketFormat) + { + if (str.StartsWith("{\"chat")) + { + chats.Enqueue(getChatJSONTag(str)); + } + } + else + { + // サポートしない, + } + } + + /// + /// 解析結果を返す がfalseならしか返さない + /// + /// 解析結果の + public NiconicoCommentXmlTag Pop() + { + return chats.Dequeue(); + } + + /// + /// で読みだすデータがあるか + /// + public bool DataAvailable() + { + return chats.Count > 0; + } + + public void Reset() + { + buffer = string.Empty; + chats.Clear(); + } + + private static ChatNiconicoCommentXmlTag getChatJSONTag(string str) { + JObject jsonObj = JObject.Parse(str); + // {"chat":{"thread":"M.kk-tzPBrneGMsNfDNO1skg","no":45929,"vpos":6496293,"date":1616936565,"date_usec":664450,"mail":"184","user_id":"EvrCRqk2e04B-pYS7q44kVU5HR4","anonymity":1,"content":"結局SBの勝ちかいw"}} + int vpos = int.Parse(jsonObj["chat"]["vpos"].ToString()); + long date = long.Parse(jsonObj["chat"]["date"].ToString()); + int dateUsec = jsonObj["chat"]["date_usec"] == null ? 0 : int.Parse(jsonObj["chat"]["date_usec"].ToString()); + string mail = jsonObj["chat"]["mail"] == null ? "" : jsonObj["chat"]["mail"].ToString(); + string userId = jsonObj["chat"]["user_id"].ToString(); + int premium = jsonObj["chat"]["premium"] == null ? 0 : int.Parse(jsonObj["chat"]["premium"].ToString()); + int anonymity = jsonObj["chat"]["anonymity"] == null ? 0 : int.Parse(jsonObj["chat"]["anonymity"].ToString()); + int abone = jsonObj["chat"]["abone"] == null ? 0 : int.Parse(jsonObj["chat"]["abone"].ToString()); + string content = (string)jsonObj["chat"]["content"]; + int no = int.Parse(jsonObj["chat"]["no"].ToString()); + return new ChatNiconicoCommentXmlTag( + content, 0, no, vpos, date, dateUsec, mail, userId, premium, anonymity, abone + ); + } + } +} diff --git a/TVTComment/TVTComment.csproj b/TVTComment/TVTComment.csproj index e4ca79a..0f15597 100644 --- a/TVTComment/TVTComment.csproj +++ b/TVTComment/TVTComment.csproj @@ -15,6 +15,7 @@ + From 49dbde5edcffa9cc3c96f6ba7ea6e7dae3b8fe04 Mon Sep 17 00:00:00 2001 From: Bob-FU <2800994+Bob-FU@users.noreply.github.com> Date: Mon, 29 Mar 2021 01:55:50 +0900 Subject: [PATCH 2/8] =?UTF-8?q?=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=81=AA=E3=81=A9=E3=81=AE=E5=BE=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Model/NiconicoUtils/NicoLiveCommentReceiver.cs | 9 ++++----- .../Model/NiconicoUtils/NiconicoCommentJsonParser.cs | 6 ++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs index 6e57e4d..4b1452b 100644 --- a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs +++ b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs @@ -102,14 +102,13 @@ public async IAsyncEnumerable Receive(string liveId, [Enu { throw new InvalidPlayerStatusNicoLiveCommentReceiverException(str.ToString()); } + // WebSocketAPIに接続 ClientWebSocket ws = new ClientWebSocket(); var uri = new Uri(msUriStr); - - //サーバに対し、接続を開始 await ws.ConnectAsync(uri, cancellationToken); var buffer = new byte[1024]; - - string body= "[{\"ping\":{\"content\":\"rs:0\"}},{\"ping\":{\"content\":\"ps:0\"}},{\"thread\":{\"thread\":\"" + threadId + "\",\"version\":\"20061206\",\"user_id\":\"guest\",\"res_from\":-10,\"with_global\":1,\"scores\":1,\"nicoru\":0}},{\"ping\":{\"content\":\"pf:0\"}},{\"ping\":{\"content\":\"rf:0\"}}]"; + // threadId情報を送信 + string body = "[{\"ping\":{\"content\":\"rs:0\"}},{\"ping\":{\"content\":\"ps:0\"}},{\"thread\":{\"thread\":\"" + threadId + "\",\"version\":\"20061206\",\"user_id\":\"guest\",\"res_from\":-10,\"with_global\":1,\"scores\":1,\"nicoru\":0}},{\"ping\":{\"content\":\"pf:0\"}},{\"ping\":{\"content\":\"rf:0\"}}]"; byte[] bodyEncoded = Encoding.UTF8.GetBytes(body); try { @@ -153,7 +152,7 @@ await ws.CloseAsync(WebSocketCloseStatus.InvalidMessageType, if (count >= buffer.Length) { await ws.CloseAsync(WebSocketCloseStatus.InvalidPayloadData, - "That's too long", CancellationToken.None); + "That's too long", cancellationToken); throw new ConnectionClosedNicoLiveCommentReceiverException(); } segment = new ArraySegment(buffer, count, buffer.Length - count); diff --git a/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs b/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs index de39872..bdb7121 100644 --- a/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs +++ b/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs @@ -10,7 +10,7 @@ class NiconicoCommentJsonParser private string buffer; /// - /// を初期化する + /// を初期化する /// /// ソケットを使うリアルタイムのデータ形式ならtrue 過去ログなどのデータ形式ならfalse public NiconicoCommentJsonParser(bool socketFormat) @@ -22,6 +22,7 @@ public void Push(string str) { if (socketFormat) { + // 一旦、コメント関連データのみ解析する if (str.StartsWith("{\"chat")) { chats.Enqueue(getChatJSONTag(str)); @@ -58,7 +59,7 @@ public void Reset() private static ChatNiconicoCommentXmlTag getChatJSONTag(string str) { JObject jsonObj = JObject.Parse(str); - // {"chat":{"thread":"M.kk-tzPBrneGMsNfDNO1skg","no":45929,"vpos":6496293,"date":1616936565,"date_usec":664450,"mail":"184","user_id":"EvrCRqk2e04B-pYS7q44kVU5HR4","anonymity":1,"content":"結局SBの勝ちかいw"}} + int vpos = int.Parse(jsonObj["chat"]["vpos"].ToString()); long date = long.Parse(jsonObj["chat"]["date"].ToString()); int dateUsec = jsonObj["chat"]["date_usec"] == null ? 0 : int.Parse(jsonObj["chat"]["date_usec"].ToString()); @@ -69,6 +70,7 @@ private static ChatNiconicoCommentXmlTag getChatJSONTag(string str) { int abone = jsonObj["chat"]["abone"] == null ? 0 : int.Parse(jsonObj["chat"]["abone"].ToString()); string content = (string)jsonObj["chat"]["content"]; int no = int.Parse(jsonObj["chat"]["no"].ToString()); + return new ChatNiconicoCommentXmlTag( content, 0, no, vpos, date, dateUsec, mail, userId, premium, anonymity, abone ); From 4141d5c016696a13047e729030c9d5ee470b8b96 Mon Sep 17 00:00:00 2001 From: Bob-FU <2800994+Bob-FU@users.noreply.github.com> Date: Mon, 29 Mar 2021 02:52:24 +0900 Subject: [PATCH 3/8] =?UTF-8?q?=E3=83=8B=E3=82=B3=E7=94=9F=E5=81=B4?= =?UTF-8?q?=E3=81=AE=E4=B8=8D=E5=85=B7=E5=90=88=E3=81=A7=E7=A8=80=E3=81=AB?= =?UTF-8?q?=E5=BF=85=E9=A0=88=E9=A0=85=E7=9B=AE=E3=81=AEvpos=E3=81=8C?= =?UTF-8?q?=E6=8A=9C=E3=81=91=E3=81=A6=E3=82=8B=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=81=8C=E6=B5=81=E3=82=8C=E3=81=A6=E3=81=8F=E3=82=8B=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E6=80=A7=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=E3=81=A7?= =?UTF-8?q?=E5=BF=B5=E3=81=AE=E7=82=BAJSON=E3=82=AD=E3=83=BC=E7=A2=BA?= =?UTF-8?q?=E8=AA=8D=E3=81=99=E3=82=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs b/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs index bdb7121..d936309 100644 --- a/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs +++ b/TVTComment/Model/NiconicoUtils/NiconicoCommentJsonParser.cs @@ -60,7 +60,7 @@ public void Reset() private static ChatNiconicoCommentXmlTag getChatJSONTag(string str) { JObject jsonObj = JObject.Parse(str); - int vpos = int.Parse(jsonObj["chat"]["vpos"].ToString()); + int vpos = jsonObj["chat"]["vpos"] == null ? 0 : int.Parse(jsonObj["chat"]["vpos"].ToString()); //ニコ生側の不具合で稀に必須項目のvposが抜けてるデータが流れてくる可能性があるので念の為JSONキー確認する。 long date = long.Parse(jsonObj["chat"]["date"].ToString()); int dateUsec = jsonObj["chat"]["date_usec"] == null ? 0 : int.Parse(jsonObj["chat"]["date_usec"].ToString()); string mail = jsonObj["chat"]["mail"] == null ? "" : jsonObj["chat"]["mail"].ToString(); From edcee915fd8c07a394f09d25c1214b6ee81b0d9f Mon Sep 17 00:00:00 2001 From: Bob-FU <2800994+Bob-FU@users.noreply.github.com> Date: Mon, 29 Mar 2021 02:53:16 +0900 Subject: [PATCH 4/8] =?UTF-8?q?RequestHeader=E3=81=AB=E5=BF=85=E8=A6=81?= =?UTF-8?q?=E3=81=AA=E3=83=98=E3=83=83=E3=83=80=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Model/NiconicoUtils/NicoLiveCommentReceiver.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs index 4b1452b..1bb4db8 100644 --- a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs +++ b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs @@ -104,9 +104,17 @@ public async IAsyncEnumerable Receive(string liveId, [Enu } // WebSocketAPIに接続 ClientWebSocket ws = new ClientWebSocket(); + // UAヘッダ追加 + ws.Options.SetRequestHeader("User-Agent", WEBSOCKET_CLIENT_UA); + // Sec-WebSocket-Protocolヘッダ追加 + ws.Options.SetRequestHeader("Sec-WebSocket-Protocol", WEBSOCKET_PROTOCOL); + // Sec-WebSocket-Versionヘッダ追加 + ws.Options.SetRequestHeader("Sec-WebSocket-Extensions", WEBSOCKET_EXTENSIONS); + var uri = new Uri(msUriStr); await ws.ConnectAsync(uri, cancellationToken); var buffer = new byte[1024]; + // threadId情報を送信 string body = "[{\"ping\":{\"content\":\"rs:0\"}},{\"ping\":{\"content\":\"ps:0\"}},{\"thread\":{\"thread\":\"" + threadId + "\",\"version\":\"20061206\",\"user_id\":\"guest\",\"res_from\":-10,\"with_global\":1,\"scores\":1,\"nicoru\":0}},{\"ping\":{\"content\":\"pf:0\"}},{\"ping\":{\"content\":\"rf:0\"}}]"; byte[] bodyEncoded = Encoding.UTF8.GetBytes(body); @@ -178,5 +186,8 @@ public void Dispose() private readonly HttpClient httpClient; private readonly NiconicoCommentJsonParser parser = new NiconicoCommentJsonParser(true); + private readonly string WEBSOCKET_CLIENT_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36"; + private readonly string WEBSOCKET_PROTOCOL = "msg.nicovideo.jp#json"; + private readonly string WEBSOCKET_EXTENSIONS = "permessage-deflate; client_max_window_bits"; } } \ No newline at end of file From 74426de4876ec72f7a3d53f0a82e1afd30a0fa22 Mon Sep 17 00:00:00 2001 From: Bob-FU <2800994+Bob-FU@users.noreply.github.com> Date: Mon, 29 Mar 2021 02:54:09 +0900 Subject: [PATCH 5/8] =?UTF-8?q?=E4=B8=87=E3=81=8C=E4=B8=80Websocket?= =?UTF-8?q?=E6=8E=A5=E7=B6=9A=E4=B8=AD=E6=96=AD=E3=81=97=E3=81=9F=E5=A0=B4?= =?UTF-8?q?=E5=90=88=E3=80=81=E6=95=B0=E7=A7=92=E7=A9=BA=E3=81=84=E3=81=9F?= =?UTF-8?q?=E3=81=8B=E3=82=89=E3=83=AA=E3=83=88=E3=83=A9=E3=82=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs index 1bb4db8..6ac64e8 100644 --- a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs +++ b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs @@ -74,6 +74,10 @@ public async IAsyncEnumerable Receive(string liveId, [Enu for (int disconnectedCount = 0; disconnectedCount < 5; ++disconnectedCount) { + // 万が一接続中断した場合、数秒空いたからリトライする。 + var random = new Random(); + await Task.Delay((disconnectedCount * 5000) + random.Next(0, 101)); + Stream str; try { From a877c9d1c87527fb6b43146ae10cee3939d042e1 Mon Sep 17 00:00:00 2001 From: Bob-FU <2800994+Bob-FU@users.noreply.github.com> Date: Mon, 29 Mar 2021 03:04:20 +0900 Subject: [PATCH 6/8] =?UTF-8?q?RequestHeader=E3=81=ABSubProtocol=E3=81=AE?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs index 6ac64e8..be4618d 100644 --- a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs +++ b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs @@ -110,8 +110,8 @@ public async IAsyncEnumerable Receive(string liveId, [Enu ClientWebSocket ws = new ClientWebSocket(); // UAヘッダ追加 ws.Options.SetRequestHeader("User-Agent", WEBSOCKET_CLIENT_UA); - // Sec-WebSocket-Protocolヘッダ追加 - ws.Options.SetRequestHeader("Sec-WebSocket-Protocol", WEBSOCKET_PROTOCOL); + // SubProtocol追加 + ws.Options.AddSubProtocol(WEBSOCKET_PROTOCOL); // Sec-WebSocket-Versionヘッダ追加 ws.Options.SetRequestHeader("Sec-WebSocket-Extensions", WEBSOCKET_EXTENSIONS); From 57d8d46ef9eaee4382da90149b02ba47b4637628 Mon Sep 17 00:00:00 2001 From: Bob-FU <2800994+Bob-FU@users.noreply.github.com> Date: Mon, 29 Mar 2021 03:53:22 +0900 Subject: [PATCH 7/8] =?UTF-8?q?Keepalive=E3=82=B3=E3=83=9E=E3=83=B3?= =?UTF-8?q?=E3=83=89=E9=80=81=E4=BF=A1=E5=87=A6=E7=90=86=E3=81=AE=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NiconicoUtils/NicoLiveCommentReceiver.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs index be4618d..a6635f3 100644 --- a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs +++ b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Net.Http; using System.Net.Sockets; @@ -58,6 +59,39 @@ public NicoLiveCommentReceiver(NiconicoLoginSession niconicoLoginSession) httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", ua); } + /// + /// KeepAliveコマンドの送信 + /// + /// + /// + private async void SendBlankAliveMessage(ClientWebSocket ws, [EnumeratorCancellation] CancellationToken cancellationToken) + { + if (ws == null || !WebSocketState.Open.Equals(ws.State)) + { + Debug.WriteLine("websocket client is in wrong state."); + return; + } + while (true) + { + try + { + await Task.Delay(60 * 1000, cancellationToken); // 1分待ちます。 + await ws.SendAsync(Encoding.UTF8.GetBytes(""), WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false); //0byteデータ送信 + } + catch (Exception e) when (e is ObjectDisposedException || e is SocketException || e is IOException || e is TaskCanceledException) + { + if (cancellationToken.IsCancellationRequested) + return; + if (e is TaskCanceledException) + return; + if (e is ObjectDisposedException) + throw; + else + throw new NetworkNicoLiveCommentReceiverException(e); + } + } + } + /// /// 受信したを無限非同期イテレータで返す /// @@ -136,6 +170,9 @@ public async IAsyncEnumerable Receive(string liveId, [Enu throw new NetworkNicoLiveCommentReceiverException(e); } + // 1分間毎に0byteのKeepAliveコマンドを送信。 + SendBlankAliveMessage(ws, cancellationToken); + //情報取得待ちループ while (true) { From beb72a56955f912cf7af831246bc3f0c5c9b59d6 Mon Sep 17 00:00:00 2001 From: Bob-FU <2800994+Bob-FU@users.noreply.github.com> Date: Mon, 29 Mar 2021 20:14:33 +0900 Subject: [PATCH 8/8] =?UTF-8?q?=E3=83=8B=E3=82=B3=E7=94=9F=E3=81=A8?= =?UTF-8?q?=E9=80=9A=E4=BF=A1=E3=81=99=E3=82=8B=E9=9A=9B=E3=81=AB=E9=80=81?= =?UTF-8?q?=E4=BF=A1=E3=81=99=E3=82=8BUA=E6=83=85=E5=A0=B1=E3=82=92?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs index a6635f3..219c9d2 100644 --- a/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs +++ b/TVTComment/Model/NiconicoUtils/NicoLiveCommentReceiver.cs @@ -143,7 +143,9 @@ public async IAsyncEnumerable Receive(string liveId, [Enu // WebSocketAPIに接続 ClientWebSocket ws = new ClientWebSocket(); // UAヘッダ追加 - ws.Options.SetRequestHeader("User-Agent", WEBSOCKET_CLIENT_UA); + var assembly = Assembly.GetExecutingAssembly().GetName(); + string version = assembly.Version.ToString(3); + ws.Options.SetRequestHeader("User-Agent", $"TvtComment/{version}"); // SubProtocol追加 ws.Options.AddSubProtocol(WEBSOCKET_PROTOCOL); // Sec-WebSocket-Versionヘッダ追加 @@ -227,7 +229,6 @@ public void Dispose() private readonly HttpClient httpClient; private readonly NiconicoCommentJsonParser parser = new NiconicoCommentJsonParser(true); - private readonly string WEBSOCKET_CLIENT_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36"; private readonly string WEBSOCKET_PROTOCOL = "msg.nicovideo.jp#json"; private readonly string WEBSOCKET_EXTENSIONS = "permessage-deflate; client_max_window_bits"; }