Dukungan WebSockets di ASP.NET Core

Artikel ini menjelaskan cara memulai WebSocket di ASP.NET Core. WebSocket (RFC 6455) adalah protokol yang memungkinkan saluran komunikasi persisten dua arah melalui koneksi TCP. Ini digunakan dalam aplikasi yang mendapat manfaat dari komunikasi yang cepat dan real time, seperti obrolan, dasbor, dan aplikasi game.

Lihat atau unduh kode sampel (cara mengunduh, cara menjalankan).

Dukungan Http/2 WebSockets

Menggunakan WebSocket melalui HTTP/2 memanfaatkan fitur baru seperti:

  • Pemadatan header.
  • Multiplexing, yang mengurangi waktu dan sumber daya yang diperlukan saat membuat beberapa permintaan ke server.

Fitur yang didukung ini tersedia di Kestrel semua platform yang diaktifkan HTTP/2. Negosiasi versi otomatis di browser dan Kestrel, sehingga tidak ada API baru yang diperlukan.

.NET 7 memperkenalkan Websocket melalui dukungan HTTP/2 untuk Kestrel, SignalR klien JavaScript, dan SignalR dengan Blazor WebAssembly.

Catatan

HTTP/2 WebSocket menggunakan permintaan CONNECT daripada GET, sehingga rute dan pengontrol Anda sendiri mungkin perlu diperbarui. Untuk informasi selengkapnya, lihat Menambahkan dukungan HTTP/2 WebSockets untuk pengontrol yang ada di artikel ini.

Chrome dan Edge mengaktifkan HTTP/2 WebSocket secara default, dan Anda dapat mengaktifkannya di FireFox di about:config halaman dengan network.http.spdy.websockets bendera.

WebSocket awalnya dirancang untuk HTTP/1.1 tetapi sejak itu telah diadaptasi untuk bekerja melalui HTTP/2. (RFC 8441)

SignalR

ASP.NET Core SignalR adalah pustaka yang menyederhanakan penambahan fungsionalitas web real time ke aplikasi. Ini menggunakan WebSockets jika memungkinkan.

Untuk sebagian besar aplikasi, kami merekomendasikan SignalR daripada WebSocket mentah. SignalR:

  • Menyediakan fallback transportasi untuk lingkungan di mana WebSocket tidak tersedia.
  • Menyediakan model aplikasi panggilan prosedur jarak jauh dasar.
  • Tidak memiliki kerugian performa yang signifikan dibandingkan dengan menggunakan WebSocket mentah dalam sebagian besar skenario.

WebSocket melalui HTTP/2 didukung untuk:

  • klien ASP.NET Core SignalR JavaScript
  • ASP.NET Core SignalR dengan Blazor WebAssembly

Untuk beberapa aplikasi, gRPC di .NET menyediakan alternatif untuk WebSockets.

Prasyarat

  • OS apa pun yang mendukung ASP.NET Core:
    • Windows 7 / Windows Server 2008 atau yang lebih baru
    • Linux
    • macOS
  • Jika aplikasi berjalan di Windows dengan IIS:
    • Windows 8 / Windows Server 2012 atau yang lebih baru
    • IIS 8 / IIS 8 Express
    • WebSocket harus diaktifkan. Lihat bagian dukungan IIS/IIS Express.
  • Jika aplikasi berjalan di HTTP.sys:
    • Windows 8 / Windows Server 2012 atau yang lebih baru
  • Untuk browser yang didukung, lihat Dapatkah saya menggunakan.

Mengonfigurasi middleware

Tambahkan middleware WebSockets di Program.cs:

app.UseWebSockets();

Pengaturan berikut dapat dikonfigurasi:

  • KeepAliveInterval - Seberapa sering mengirim bingkai "ping" ke klien untuk memastikan proksi menjaga koneksi tetap terbuka. Defaultnya adalah dua menit.
  • AllowedOrigins - Daftar nilai header Asal yang diizinkan untuk permintaan WebSocket. Secara default, semua asal diizinkan. Untuk informasi selengkapnya, lihat Pembatasan asal WebSocket di artikel ini.
var webSocketOptions = new WebSocketOptions
{
    KeepAliveInterval = TimeSpan.FromMinutes(2)
};

app.UseWebSockets(webSocketOptions);

Menerima permintaan WebSocket

Di suatu tempat nanti dalam siklus hidup permintaan (nanti dalam Program.cs atau dalam metode tindakan, misalnya) periksa apakah itu permintaan WebSocket dan terima permintaan WebSocket.

Contoh berikut adalah dari nanti di Program.cs:

app.Use(async (context, next) =>
{
    if (context.Request.Path == "/ws")
    {
        if (context.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            context.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }
    else
    {
        await next(context);
    }

});

Permintaan WebSocket dapat masuk pada URL apa pun, tetapi kode sampel ini hanya menerima permintaan untuk /ws.

Pendekatan serupa dapat diambil dalam metode pengontrol:

public class WebSocketController : ControllerBase
{
    [Route("/ws")]
    public async Task Get()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }

Saat menggunakan WebSocket, Anda harus menjaga alur middleware tetap berjalan selama durasi koneksi. Jika Anda mencoba mengirim atau menerima pesan WebSocket setelah alur middleware berakhir, Anda mungkin mendapatkan pengecualian seperti berikut ini:

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.

Jika Anda menggunakan layanan latar belakang untuk menulis data ke WebSocket, pastikan Anda menjaga alur middleware tetap berjalan. Lakukan ini dengan menggunakan TaskCompletionSource<TResult>. Teruskan TaskCompletionSource ke layanan latar belakang Anda dan panggil TrySetResult saat Anda selesai dengan WebSocket. Task Kemudian await properti selama permintaan, seperti yang ditunjukkan dalam contoh berikut:

app.Run(async (context) =>
{
    using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
    var socketFinishedTcs = new TaskCompletionSource<object>();

    BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);

    await socketFinishedTcs.Task;
});

Pengecualian tertutup WebSocket juga dapat terjadi ketika mengembalikan terlalu cepat dari metode tindakan. Saat menerima soket dalam metode tindakan, tunggu kode yang menggunakan soket selesai sebelum kembali dari metode tindakan.

Jangan pernah menggunakan Task.Wait, Task.Result, atau panggilan pemblokiran serupa untuk menunggu soket selesai, karena dapat menyebabkan masalah utas yang serius. Selalu gunakan await.

Menambahkan dukungan HTTP/2 WebSockets untuk pengontrol yang ada

.NET 7 memperkenalkan Websocket melalui dukungan HTTP/2 untuk Kestrel, SignalR klien JavaScript, dan SignalR dengan Blazor WebAssembly. HTTP/2 WebSocket menggunakan permintaan CONNECT daripada GET. Jika sebelumnya Anda menggunakan [HttpGet("/path")] metode tindakan pengontrol untuk permintaan Websocket, perbarui untuk digunakan [Route("/path")] sebagai gantinya.

public class WebSocketController : ControllerBase
{
    [Route("/ws")]
    public async Task Get()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }

Kompresi

Peringatan

Mengaktifkan kompresi melalui koneksi terenkripsi dapat membuat aplikasi tunduk pada CRIME/BREACH serangan. Jika mengirim informasi sensitif, hindari mengaktifkan pemadatan atau penggunaan WebSocketMessageFlags.DisableCompression saat memanggil WebSocket.SendAsync. Ini berlaku untuk kedua sisi WebSocket. Perhatikan bahwa API WebSockets di browser tidak memiliki konfigurasi untuk menonaktifkan kompresi per pengiriman.

Jika pemadatan pesan melalui WebSockets diinginkan, maka kode terima harus menentukan bahwa itu memungkinkan pemadatan sebagai berikut:

using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
    new WebSocketAcceptContext { DangerousEnableCompression = true }))
{

}

WebSocketAcceptContext.ServerMaxWindowBits dan WebSocketAcceptContext.DisableServerContextTakeover merupakan opsi lanjutan yang mengontrol cara kerja pemadatan.

Kompresi dinegosiasikan antara klien dan server saat pertama kali membuat koneksi. Anda dapat membaca selengkapnya tentang negosiasi di Ekstensi Kompresi untuk WebSocket RFC.

Catatan

Jika negosiasi kompresi tidak diterima oleh server atau klien, koneksi masih dibuat. Namun, koneksi tidak menggunakan pemadatan saat mengirim dan menerima pesan.

Mengirim dan menerima pesan

Metode ini AcceptWebSocketAsync meningkatkan koneksi TCP ke koneksi WebSocket dan menyediakan WebSocket objek. WebSocket Gunakan objek untuk mengirim dan menerima pesan.

Kode yang ditampilkan sebelumnya yang menerima permintaan WebSocket meneruskan objek ke WebSocketEcho metode . Kode menerima pesan dan segera mengirim kembali pesan yang sama. Pesan dikirim dan diterima dalam perulangan hingga klien menutup koneksi:

private static async Task Echo(WebSocket webSocket)
{
    var buffer = new byte[1024 * 4];
    var receiveResult = await webSocket.ReceiveAsync(
        new ArraySegment<byte>(buffer), CancellationToken.None);

    while (!receiveResult.CloseStatus.HasValue)
    {
        await webSocket.SendAsync(
            new ArraySegment<byte>(buffer, 0, receiveResult.Count),
            receiveResult.MessageType,
            receiveResult.EndOfMessage,
            CancellationToken.None);

        receiveResult = await webSocket.ReceiveAsync(
            new ArraySegment<byte>(buffer), CancellationToken.None);
    }

    await webSocket.CloseAsync(
        receiveResult.CloseStatus.Value,
        receiveResult.CloseStatusDescription,
        CancellationToken.None);
}

Saat menerima koneksi WebSocket sebelum memulai perulangan, alur middleware berakhir. Setelah menutup soket, alur melepas lelah. Artinya, permintaan berhenti bergerak maju dalam alur ketika WebSocket diterima. Ketika perulangan selesai dan soket ditutup, permintaan melanjutkan pencadangan alur.

Menangani pemutusan sambungan klien

Server tidak secara otomatis diberi tahu ketika klien terputus karena hilangnya konektivitas. Server menerima pesan pemutusan sambungan hanya jika klien mengirimkannya, yang tidak dapat dilakukan jika koneksi internet hilang. Jika Anda ingin mengambil beberapa tindakan ketika itu terjadi, atur batas waktu setelah tidak ada yang diterima dari klien dalam jendela waktu tertentu.

Jika klien tidak selalu mengirim pesan dan Anda tidak ingin kehabisan waktu hanya karena koneksi tidak aktif, minta klien menggunakan timer untuk mengirim pesan ping setiap X detik. Di server, jika pesan belum tiba dalam waktu 2*X detik setelah yang sebelumnya, hentikan koneksi dan laporkan bahwa klien terputus. Tunggu dua kali interval waktu yang diharapkan untuk menyisakan waktu tambahan untuk penundaan jaringan yang mungkin menahan pesan ping.

Pembatasan asal WebSocket

Perlindungan yang diberikan oleh CORS tidak berlaku untuk WebSocket. Browser tidak:

  • Lakukan permintaan pra-penerbangan CORS.
  • Hormati batasan yang ditentukan dalam Access-Control header saat membuat permintaan WebSocket.

Namun, browser mengirim Origin header saat mengeluarkan permintaan WebSocket. Aplikasi harus dikonfigurasi untuk memvalidasi header ini untuk memastikan bahwa hanya WebSocket yang berasal dari asal yang diharapkan yang diizinkan.

Jika Anda menghosting server Anda di "https://server.com" dan hosting klien Anda di "https://client.com", tambahkan "https://client.com" ke AllowedOrigins daftar untuk WebSockets untuk memverifikasi.

var webSocketOptions = new WebSocketOptions
{
    KeepAliveInterval = TimeSpan.FromMinutes(2)
};

webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");

app.UseWebSockets(webSocketOptions);

Catatan

Header Origin dikontrol oleh klien dan, seperti Referer header, dapat dipalsahkan. Jangan gunakan header ini sebagai mekanisme autentikasi.

Dukungan IIS/IIS Express

Windows Server 2012 atau yang lebih baru dan Windows 8 atau yang lebih baru dengan IIS/IIS Express 8 atau yang lebih baru memiliki dukungan untuk protokol WebSocket, tetapi tidak untuk WebSocket melalui HTTP/2.

Catatan

WebSocket selalu diaktifkan saat menggunakan IIS Express.

Mengaktifkan WebSocket di IIS

Untuk mengaktifkan dukungan untuk protokol WebSocket pada Windows Server 2012 atau yang lebih baru:

Catatan

Langkah-langkah ini tidak diperlukan saat menggunakan IIS Express

  1. Gunakan wizard Tambahkan Peran dan Fitur dari menu Kelola atau tautan di Pengelola Server.
  2. Pilih Penginstalan berbasis peran atau Berbasis fitur. Pilih Selanjutnya.
  3. Pilih server yang sesuai (server lokal dipilih secara default). Pilih Selanjutnya.
  4. Perluas Server Web (IIS) di pohon Peran , perluas Server Web, lalu perluas Pengembangan Aplikasi.
  5. Pilih Protokol WebSocket. Pilih Selanjutnya.
  6. Jika fitur tambahan tidak diperlukan, pilih Berikutnya.
  7. Pilih Instal.
  8. Saat penginstalan selesai, pilih Tutup untuk keluar dari wizard.

Untuk mengaktifkan dukungan untuk protokol WebSocket pada Windows 8 atau yang lebih baru:

Catatan

Langkah-langkah ini tidak diperlukan saat menggunakan IIS Express

  1. Buka Panel Kontrol>Program>Program dan Fitur>Aktifkan atau nonaktifkan fitur Windows (sisi kiri layar).
  2. Buka simpul berikut: Layanan Informasi Internet> World Wide Web Services>Application Development Features.
  3. Pilih fitur Protokol WebSocket. Pilih OK.

Nonaktifkan WebSocket saat menggunakan socket.io di Node.js

Jika menggunakan dukungan WebSocket di socket.io pada Node.js, nonaktifkan modul IIS WebSocket default menggunakan webSocket elemen di web.config atau applicationHost.config. Jika langkah ini tidak dilakukan, modul IIS WebSocket mencoba menangani komunikasi WebSocket daripada Node.js dan aplikasi.

<system.webServer>
  <webSocket enabled="false" />
</system.webServer>

Aplikasi sampel

Aplikasi sampel yang menyertai artikel ini adalah aplikasi echo. Ini memiliki halaman web yang membuat koneksi WebSocket, dan server mengirim ulang pesan apa pun yang diterimanya kembali ke klien. Aplikasi sampel mendukung WebSocket melalui HTTP/2 saat menggunakan kerangka kerja yang ditargetkan dari .NET 7 atau yang lebih baru.

Jalankan aplikasi:

  • Untuk menjalankan aplikasi di Visual Studio: Buka proyek sampel di Visual Studio, dan tekan Ctrl+F5 untuk berjalan tanpa debugger.
  • Untuk menjalankan aplikasi di shell perintah: Jalankan perintah dotnet run dan navigasikan di browser ke http://localhost:<port>.

Halaman web menunjukkan status koneksi:

Status awal halaman web sebelum koneksi WebSockets

Pilih Koneksi untuk mengirim permintaan WebSocket ke URL yang ditampilkan. Masukkan pesan pengujian dan pilih Kirim. Setelah selesai, pilih Tutup Soket. Bagian Log Komunikasi melaporkan setiap tindakan terbuka, kirim, dan tutup saat terjadi.

Status akhir halaman web setelah koneksi WebSocket dan pesan pengujian dikirim dan diterima

Artikel ini menjelaskan cara memulai WebSocket di ASP.NET Core. WebSocket (RFC 6455) adalah protokol yang memungkinkan saluran komunikasi persisten dua arah melalui koneksi TCP. Ini digunakan dalam aplikasi yang mendapat manfaat dari komunikasi yang cepat dan real time, seperti obrolan, dasbor, dan aplikasi game.

Lihat atau unduh kode sampel (cara mengunduh, cara menjalankan).

Dukungan Http/2 WebSockets

Menggunakan WebSocket melalui HTTP/2 memanfaatkan fitur baru seperti:

  • Pemadatan header.
  • Multiplexing, yang mengurangi waktu dan sumber daya yang diperlukan saat membuat beberapa permintaan ke server.

Fitur yang didukung ini tersedia di Kestrel semua platform yang diaktifkan HTTP/2. Negosiasi versi otomatis di browser dan Kestrel, sehingga tidak ada API baru yang diperlukan.

.NET 7 memperkenalkan Websocket melalui dukungan HTTP/2 untuk Kestrel, SignalR klien JavaScript, dan SignalR dengan Blazor WebAssembly.

Catatan

HTTP/2 WebSocket menggunakan permintaan CONNECT daripada GET, sehingga rute dan pengontrol Anda sendiri mungkin perlu diperbarui. Untuk informasi selengkapnya, lihat Menambahkan dukungan HTTP/2 WebSockets untuk pengontrol yang ada di artikel ini.

Chrome dan Edge mengaktifkan HTTP/2 WebSocket secara default, dan Anda dapat mengaktifkannya di FireFox di about:config halaman dengan network.http.spdy.websockets bendera.

WebSocket awalnya dirancang untuk HTTP/1.1 tetapi sejak itu telah diadaptasi untuk bekerja melalui HTTP/2. (RFC 8441)

SignalR

ASP.NET Core SignalR adalah pustaka yang menyederhanakan penambahan fungsionalitas web real time ke aplikasi. Ini menggunakan WebSockets jika memungkinkan.

Untuk sebagian besar aplikasi, kami merekomendasikan SignalR daripada WebSocket mentah. SignalR:

  • Menyediakan fallback transportasi untuk lingkungan di mana WebSocket tidak tersedia.
  • Menyediakan model aplikasi panggilan prosedur jarak jauh dasar.
  • Tidak memiliki kerugian performa yang signifikan dibandingkan dengan menggunakan WebSocket mentah dalam sebagian besar skenario.

WebSocket melalui HTTP/2 didukung untuk:

  • klien ASP.NET Core SignalR JavaScript
  • ASP.NET Core SignalR dengan Blazor WebAssembly

Untuk beberapa aplikasi, gRPC di .NET menyediakan alternatif untuk WebSockets.

Prasyarat

  • OS apa pun yang mendukung ASP.NET Core:
    • Windows 7 / Windows Server 2008 atau yang lebih baru
    • Linux
    • macOS
  • Jika aplikasi berjalan di Windows dengan IIS:
    • Windows 8 / Windows Server 2012 atau yang lebih baru
    • IIS 8 / IIS 8 Express
    • WebSocket harus diaktifkan. Lihat bagian dukungan IIS/IIS Express.
  • Jika aplikasi berjalan di HTTP.sys:
    • Windows 8 / Windows Server 2012 atau yang lebih baru
  • Untuk browser yang didukung, lihat Dapatkah saya menggunakan.

Mengonfigurasi middleware

Tambahkan middleware WebSockets di Program.cs:

app.UseWebSockets();

Pengaturan berikut dapat dikonfigurasi:

  • KeepAliveInterval - Seberapa sering mengirim bingkai "ping" ke klien untuk memastikan proksi menjaga koneksi tetap terbuka. Defaultnya adalah dua menit.
  • AllowedOrigins - Daftar nilai header Asal yang diizinkan untuk permintaan WebSocket. Secara default, semua asal diizinkan. Untuk informasi selengkapnya, lihat Pembatasan asal WebSocket di artikel ini.
var webSocketOptions = new WebSocketOptions
{
    KeepAliveInterval = TimeSpan.FromMinutes(2)
};

app.UseWebSockets(webSocketOptions);

Menerima permintaan WebSocket

Di suatu tempat nanti dalam siklus hidup permintaan (nanti dalam Program.cs atau dalam metode tindakan, misalnya) periksa apakah itu permintaan WebSocket dan terima permintaan WebSocket.

Contoh berikut adalah dari nanti di Program.cs:

app.Use(async (context, next) =>
{
    if (context.Request.Path == "/ws")
    {
        if (context.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            context.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }
    else
    {
        await next(context);
    }

});

Permintaan WebSocket dapat masuk pada URL apa pun, tetapi kode sampel ini hanya menerima permintaan untuk /ws.

Pendekatan serupa dapat diambil dalam metode pengontrol:

public class WebSocketController : ControllerBase
{
    [Route("/ws")]
    public async Task Get()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }

Saat menggunakan WebSocket, Anda harus menjaga alur middleware tetap berjalan selama durasi koneksi. Jika Anda mencoba mengirim atau menerima pesan WebSocket setelah alur middleware berakhir, Anda mungkin mendapatkan pengecualian seperti berikut ini:

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.

Jika Anda menggunakan layanan latar belakang untuk menulis data ke WebSocket, pastikan Anda menjaga alur middleware tetap berjalan. Lakukan ini dengan menggunakan TaskCompletionSource<TResult>. Teruskan TaskCompletionSource ke layanan latar belakang Anda dan panggil TrySetResult saat Anda selesai dengan WebSocket. Task Kemudian await properti selama permintaan, seperti yang ditunjukkan dalam contoh berikut:

app.Run(async (context) =>
{
    using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
    var socketFinishedTcs = new TaskCompletionSource<object>();

    BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);

    await socketFinishedTcs.Task;
});

Pengecualian tertutup WebSocket juga dapat terjadi ketika mengembalikan terlalu cepat dari metode tindakan. Saat menerima soket dalam metode tindakan, tunggu kode yang menggunakan soket selesai sebelum kembali dari metode tindakan.

Jangan pernah menggunakan Task.Wait, Task.Result, atau panggilan pemblokiran serupa untuk menunggu soket selesai, karena dapat menyebabkan masalah utas yang serius. Selalu gunakan await.

Menambahkan dukungan HTTP/2 WebSockets untuk pengontrol yang ada

.NET 7 memperkenalkan Websocket melalui dukungan HTTP/2 untuk Kestrel, SignalR klien JavaScript, dan SignalR dengan Blazor WebAssembly. HTTP/2 WebSocket menggunakan permintaan CONNECT daripada GET. Jika sebelumnya Anda menggunakan [HttpGet("/path")] metode tindakan pengontrol untuk permintaan Websocket, perbarui untuk digunakan [Route("/path")] sebagai gantinya.

public class WebSocketController : ControllerBase
{
    [Route("/ws")]
    public async Task Get()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }

Kompresi

Peringatan

Mengaktifkan kompresi melalui koneksi terenkripsi dapat membuat aplikasi tunduk pada CRIME/BREACH serangan. Jika mengirim informasi sensitif, hindari mengaktifkan pemadatan atau penggunaan WebSocketMessageFlags.DisableCompression saat memanggil WebSocket.SendAsync. Ini berlaku untuk kedua sisi WebSocket. Perhatikan bahwa API WebSockets di browser tidak memiliki konfigurasi untuk menonaktifkan kompresi per pengiriman.

Jika pemadatan pesan melalui WebSockets diinginkan, maka kode terima harus menentukan bahwa itu memungkinkan pemadatan sebagai berikut:

using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
    new WebSocketAcceptContext { DangerousEnableCompression = true }))
{

}

WebSocketAcceptContext.ServerMaxWindowBits dan WebSocketAcceptContext.DisableServerContextTakeover merupakan opsi lanjutan yang mengontrol cara kerja pemadatan.

Kompresi dinegosiasikan antara klien dan server saat pertama kali membuat koneksi. Anda dapat membaca selengkapnya tentang negosiasi di Ekstensi Kompresi untuk WebSocket RFC.

Catatan

Jika negosiasi kompresi tidak diterima oleh server atau klien, koneksi masih dibuat. Namun, koneksi tidak menggunakan pemadatan saat mengirim dan menerima pesan.

Mengirim dan menerima pesan

Metode ini AcceptWebSocketAsync meningkatkan koneksi TCP ke koneksi WebSocket dan menyediakan WebSocket objek. WebSocket Gunakan objek untuk mengirim dan menerima pesan.

Kode yang ditampilkan sebelumnya yang menerima permintaan WebSocket meneruskan objek ke WebSocketEcho metode . Kode menerima pesan dan segera mengirim kembali pesan yang sama. Pesan dikirim dan diterima dalam perulangan hingga klien menutup koneksi:

private static async Task Echo(WebSocket webSocket)
{
    var buffer = new byte[1024 * 4];
    var receiveResult = await webSocket.ReceiveAsync(
        new ArraySegment<byte>(buffer), CancellationToken.None);

    while (!receiveResult.CloseStatus.HasValue)
    {
        await webSocket.SendAsync(
            new ArraySegment<byte>(buffer, 0, receiveResult.Count),
            receiveResult.MessageType,
            receiveResult.EndOfMessage,
            CancellationToken.None);

        receiveResult = await webSocket.ReceiveAsync(
            new ArraySegment<byte>(buffer), CancellationToken.None);
    }

    await webSocket.CloseAsync(
        receiveResult.CloseStatus.Value,
        receiveResult.CloseStatusDescription,
        CancellationToken.None);
}

Saat menerima koneksi WebSocket sebelum memulai perulangan, alur middleware berakhir. Setelah menutup soket, alur melepas lelah. Artinya, permintaan berhenti bergerak maju dalam alur ketika WebSocket diterima. Ketika perulangan selesai dan soket ditutup, permintaan melanjutkan pencadangan alur.

Menangani pemutusan sambungan klien

Server tidak secara otomatis diberi tahu ketika klien terputus karena hilangnya konektivitas. Server menerima pesan pemutusan sambungan hanya jika klien mengirimkannya, yang tidak dapat dilakukan jika koneksi internet hilang. Jika Anda ingin mengambil beberapa tindakan ketika itu terjadi, atur batas waktu setelah tidak ada yang diterima dari klien dalam jendela waktu tertentu.

Jika klien tidak selalu mengirim pesan dan Anda tidak ingin kehabisan waktu hanya karena koneksi tidak aktif, minta klien menggunakan timer untuk mengirim pesan ping setiap X detik. Di server, jika pesan belum tiba dalam waktu 2*X detik setelah yang sebelumnya, hentikan koneksi dan laporkan bahwa klien terputus. Tunggu dua kali interval waktu yang diharapkan untuk menyisakan waktu tambahan untuk penundaan jaringan yang mungkin menahan pesan ping.

Pembatasan asal WebSocket

Perlindungan yang diberikan oleh CORS tidak berlaku untuk WebSocket. Browser tidak:

  • Lakukan permintaan pra-penerbangan CORS.
  • Hormati batasan yang ditentukan dalam Access-Control header saat membuat permintaan WebSocket.

Namun, browser mengirim Origin header saat mengeluarkan permintaan WebSocket. Aplikasi harus dikonfigurasi untuk memvalidasi header ini untuk memastikan bahwa hanya WebSocket yang berasal dari asal yang diharapkan yang diizinkan.

Jika Anda menghosting server Anda di "https://server.com" dan hosting klien Anda di "https://client.com", tambahkan "https://client.com" ke AllowedOrigins daftar untuk WebSockets untuk memverifikasi.

var webSocketOptions = new WebSocketOptions
{
    KeepAliveInterval = TimeSpan.FromMinutes(2)
};

webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");

app.UseWebSockets(webSocketOptions);

Catatan

Header Origin dikontrol oleh klien dan, seperti Referer header, dapat dipalsahkan. Jangan gunakan header ini sebagai mekanisme autentikasi.

Dukungan IIS/IIS Express

Windows Server 2012 atau yang lebih baru dan Windows 8 atau yang lebih baru dengan IIS/IIS Express 8 atau yang lebih baru memiliki dukungan untuk protokol WebSocket, tetapi tidak untuk WebSocket melalui HTTP/2.

Catatan

WebSocket selalu diaktifkan saat menggunakan IIS Express.

Mengaktifkan WebSocket di IIS

Untuk mengaktifkan dukungan untuk protokol WebSocket pada Windows Server 2012 atau yang lebih baru:

Catatan

Langkah-langkah ini tidak diperlukan saat menggunakan IIS Express

  1. Gunakan wizard Tambahkan Peran dan Fitur dari menu Kelola atau tautan di Pengelola Server.
  2. Pilih Penginstalan berbasis peran atau Berbasis fitur. Pilih Selanjutnya.
  3. Pilih server yang sesuai (server lokal dipilih secara default). Pilih Selanjutnya.
  4. Perluas Server Web (IIS) di pohon Peran , perluas Server Web, lalu perluas Pengembangan Aplikasi.
  5. Pilih Protokol WebSocket. Pilih Selanjutnya.
  6. Jika fitur tambahan tidak diperlukan, pilih Berikutnya.
  7. Pilih Instal.
  8. Saat penginstalan selesai, pilih Tutup untuk keluar dari wizard.

Untuk mengaktifkan dukungan untuk protokol WebSocket pada Windows 8 atau yang lebih baru:

Catatan

Langkah-langkah ini tidak diperlukan saat menggunakan IIS Express

  1. Buka Panel Kontrol>Program>Program dan Fitur>Aktifkan atau nonaktifkan fitur Windows (sisi kiri layar).
  2. Buka simpul berikut: Layanan Informasi Internet> World Wide Web Services>Application Development Features.
  3. Pilih fitur Protokol WebSocket. Pilih OK.

Nonaktifkan WebSocket saat menggunakan socket.io di Node.js

Jika menggunakan dukungan WebSocket di socket.io pada Node.js, nonaktifkan modul IIS WebSocket default menggunakan webSocket elemen di web.config atau applicationHost.config. Jika langkah ini tidak dilakukan, modul IIS WebSocket mencoba menangani komunikasi WebSocket daripada Node.js dan aplikasi.

<system.webServer>
  <webSocket enabled="false" />
</system.webServer>

Aplikasi sampel

Aplikasi sampel yang menyertai artikel ini adalah aplikasi echo. Ini memiliki halaman web yang membuat koneksi WebSocket, dan server mengirim ulang pesan apa pun yang diterimanya kembali ke klien. Aplikasi sampel mendukung WebSocket melalui HTTP/2 saat menggunakan kerangka kerja yang ditargetkan dari .NET 7 atau yang lebih baru.

Jalankan aplikasi:

  • Untuk menjalankan aplikasi di Visual Studio: Buka proyek sampel di Visual Studio, dan tekan Ctrl+F5 untuk berjalan tanpa debugger.
  • Untuk menjalankan aplikasi di shell perintah: Jalankan perintah dotnet run dan navigasikan di browser ke http://localhost:<port>.

Halaman web menunjukkan status koneksi:

Status awal halaman web sebelum koneksi WebSockets

Pilih Koneksi untuk mengirim permintaan WebSocket ke URL yang ditampilkan. Masukkan pesan pengujian dan pilih Kirim. Setelah selesai, pilih Tutup Soket. Bagian Log Komunikasi melaporkan setiap tindakan terbuka, kirim, dan tutup saat terjadi.

Status akhir halaman web setelah koneksi WebSocket dan pesan pengujian dikirim dan diterima

Artikel ini menjelaskan cara memulai WebSocket di ASP.NET Core. WebSocket (RFC 6455) adalah protokol yang memungkinkan saluran komunikasi persisten dua arah melalui koneksi TCP. Ini digunakan dalam aplikasi yang mendapat manfaat dari komunikasi yang cepat dan real time, seperti obrolan, dasbor, dan aplikasi game.

Lihat atau unduh kode sampel (cara mengunduh, cara menjalankan).

SignalR

ASP.NET Core SignalR adalah pustaka yang menyederhanakan penambahan fungsionalitas web real time ke aplikasi. Ini menggunakan WebSockets jika memungkinkan.

Untuk sebagian besar aplikasi, kami merekomendasikan SignalR lebih dari WebSocket mentah. SignalR menyediakan fallback transportasi untuk lingkungan di mana WebSocket tidak tersedia. Ini juga menyediakan model aplikasi panggilan prosedur jarak jauh dasar. Dan dalam sebagian besar skenario, SignalR tidak memiliki kerugian performa yang signifikan dibandingkan dengan menggunakan WebSocket mentah.

Untuk beberapa aplikasi, gRPC di .NET menyediakan alternatif untuk WebSockets.

Prasyarat

  • OS apa pun yang mendukung ASP.NET Core:
    • Windows 7 / Windows Server 2008 atau yang lebih baru
    • Linux
    • macOS
  • Jika aplikasi berjalan di Windows dengan IIS:
    • Windows 8 / Windows Server 2012 atau yang lebih baru
    • IIS 8 / IIS 8 Express
    • WebSocket harus diaktifkan. Lihat bagian dukungan IIS/IIS Express.
  • Jika aplikasi berjalan di HTTP.sys:
    • Windows 8 / Windows Server 2012 atau yang lebih baru
  • Untuk browser yang didukung, lihat Dapatkah saya menggunakan.

Mengonfigurasi middleware

Tambahkan middleware WebSockets di Program.cs:

app.UseWebSockets();

Pengaturan berikut dapat dikonfigurasi:

  • KeepAliveInterval - Seberapa sering mengirim bingkai "ping" ke klien untuk memastikan proksi menjaga koneksi tetap terbuka. Defaultnya adalah dua menit.
  • AllowedOrigins - Daftar nilai header Asal yang diizinkan untuk permintaan WebSocket. Secara default, semua asal diizinkan. Untuk informasi selengkapnya, lihat Pembatasan asal WebSocket di artikel ini.
var webSocketOptions = new WebSocketOptions
{
    KeepAliveInterval = TimeSpan.FromMinutes(2)
};

app.UseWebSockets(webSocketOptions);

Menerima permintaan WebSocket

Di suatu tempat nanti dalam siklus hidup permintaan (nanti dalam Program.cs atau dalam metode tindakan, misalnya) periksa apakah itu permintaan WebSocket dan terima permintaan WebSocket.

Contoh berikut adalah dari nanti di Program.cs:

app.Use(async (context, next) =>
{
    if (context.Request.Path == "/ws")
    {
        if (context.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            context.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }
    else
    {
        await next(context);
    }

});

Permintaan WebSocket dapat masuk pada URL apa pun, tetapi kode sampel ini hanya menerima permintaan untuk /ws.

Pendekatan serupa dapat diambil dalam metode pengontrol:

public class WebSocketController : ControllerBase
{
    [HttpGet("/ws")]
    public async Task Get()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }

Saat menggunakan WebSocket, Anda harus menjaga alur middleware tetap berjalan selama durasi koneksi. Jika Anda mencoba mengirim atau menerima pesan WebSocket setelah alur middleware berakhir, Anda mungkin mendapatkan pengecualian seperti berikut ini:

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.

Jika Anda menggunakan layanan latar belakang untuk menulis data ke WebSocket, pastikan Anda menjaga alur middleware tetap berjalan. Lakukan ini dengan menggunakan TaskCompletionSource<TResult>. Teruskan TaskCompletionSource ke layanan latar belakang Anda dan panggil TrySetResult saat Anda selesai dengan WebSocket. Task Kemudian await properti selama permintaan, seperti yang ditunjukkan dalam contoh berikut:

app.Run(async (context) =>
{
    using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
    var socketFinishedTcs = new TaskCompletionSource<object>();

    BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);

    await socketFinishedTcs.Task;
});

Pengecualian tertutup WebSocket juga dapat terjadi ketika mengembalikan terlalu cepat dari metode tindakan. Saat menerima soket dalam metode tindakan, tunggu kode yang menggunakan soket selesai sebelum kembali dari metode tindakan.

Jangan pernah menggunakan Task.Wait, Task.Result, atau panggilan pemblokiran serupa untuk menunggu soket selesai, karena dapat menyebabkan masalah utas yang serius. Selalu gunakan await.

Kompresi

Peringatan

Mengaktifkan kompresi melalui koneksi terenkripsi dapat membuat aplikasi tunduk pada CRIME/BREACH serangan. Jika mengirim informasi sensitif, hindari mengaktifkan pemadatan atau penggunaan WebSocketMessageFlags.DisableCompression saat memanggil WebSocket.SendAsync. Ini berlaku untuk kedua sisi WebSocket. Perhatikan bahwa API WebSockets di browser tidak memiliki konfigurasi untuk menonaktifkan kompresi per pengiriman.

Jika pemadatan pesan melalui WebSockets diinginkan, maka kode terima harus menentukan bahwa itu memungkinkan pemadatan sebagai berikut:

using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
    new WebSocketAcceptContext { DangerousEnableCompression = true }))
{

}

WebSocketAcceptContext.ServerMaxWindowBits dan WebSocketAcceptContext.DisableServerContextTakeover merupakan opsi lanjutan yang mengontrol cara kerja pemadatan.

Kompresi dinegosiasikan antara klien dan server saat pertama kali membuat koneksi. Anda dapat membaca selengkapnya tentang negosiasi di Ekstensi Kompresi untuk WebSocket RFC.

Catatan

Jika negosiasi kompresi tidak diterima oleh server atau klien, koneksi masih dibuat. Namun, koneksi tidak menggunakan pemadatan saat mengirim dan menerima pesan.

Mengirim dan menerima pesan

Metode ini AcceptWebSocketAsync meningkatkan koneksi TCP ke koneksi WebSocket dan menyediakan WebSocket objek. WebSocket Gunakan objek untuk mengirim dan menerima pesan.

Kode yang ditampilkan sebelumnya yang menerima permintaan WebSocket meneruskan objek ke WebSocketEcho metode . Kode menerima pesan dan segera mengirim kembali pesan yang sama. Pesan dikirim dan diterima dalam perulangan hingga klien menutup koneksi:

private static async Task Echo(WebSocket webSocket)
{
    var buffer = new byte[1024 * 4];
    var receiveResult = await webSocket.ReceiveAsync(
        new ArraySegment<byte>(buffer), CancellationToken.None);

    while (!receiveResult.CloseStatus.HasValue)
    {
        await webSocket.SendAsync(
            new ArraySegment<byte>(buffer, 0, receiveResult.Count),
            receiveResult.MessageType,
            receiveResult.EndOfMessage,
            CancellationToken.None);

        receiveResult = await webSocket.ReceiveAsync(
            new ArraySegment<byte>(buffer), CancellationToken.None);
    }

    await webSocket.CloseAsync(
        receiveResult.CloseStatus.Value,
        receiveResult.CloseStatusDescription,
        CancellationToken.None);
}

Saat menerima koneksi WebSocket sebelum memulai perulangan, alur middleware berakhir. Setelah menutup soket, alur melepas lelah. Artinya, permintaan berhenti bergerak maju dalam alur ketika WebSocket diterima. Ketika perulangan selesai dan soket ditutup, permintaan melanjutkan pencadangan alur.

Menangani pemutusan sambungan klien

Server tidak secara otomatis diberi tahu ketika klien terputus karena hilangnya konektivitas. Server menerima pesan pemutusan sambungan hanya jika klien mengirimkannya, yang tidak dapat dilakukan jika koneksi internet hilang. Jika Anda ingin mengambil beberapa tindakan ketika itu terjadi, atur batas waktu setelah tidak ada yang diterima dari klien dalam jendela waktu tertentu.

Jika klien tidak selalu mengirim pesan dan Anda tidak ingin kehabisan waktu hanya karena koneksi tidak aktif, minta klien menggunakan timer untuk mengirim pesan ping setiap X detik. Di server, jika pesan belum tiba dalam waktu 2*X detik setelah yang sebelumnya, hentikan koneksi dan laporkan bahwa klien terputus. Tunggu dua kali interval waktu yang diharapkan untuk menyisakan waktu tambahan untuk penundaan jaringan yang mungkin menahan pesan ping.

Pembatasan asal WebSocket

Perlindungan yang diberikan oleh CORS tidak berlaku untuk WebSocket. Browser tidak:

  • Lakukan permintaan pra-penerbangan CORS.
  • Hormati batasan yang ditentukan dalam Access-Control header saat membuat permintaan WebSocket.

Namun, browser mengirim Origin header saat mengeluarkan permintaan WebSocket. Aplikasi harus dikonfigurasi untuk memvalidasi header ini untuk memastikan bahwa hanya WebSocket yang berasal dari asal yang diharapkan yang diizinkan.

Jika Anda menghosting server Anda di "https://server.com" dan hosting klien Anda di "https://client.com", tambahkan "https://client.com" ke AllowedOrigins daftar untuk WebSockets untuk memverifikasi.

var webSocketOptions = new WebSocketOptions
{
    KeepAliveInterval = TimeSpan.FromMinutes(2)
};

webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");

app.UseWebSockets(webSocketOptions);

Catatan

Header Origin dikontrol oleh klien dan, seperti Referer header, dapat dipalsahkan. Jangan gunakan header ini sebagai mekanisme autentikasi.

Dukungan IIS/IIS Express

Windows Server 2012 atau yang lebih baru dan Windows 8 atau yang lebih baru dengan IIS/IIS Express 8 atau yang lebih baru memiliki dukungan untuk protokol WebSocket.

Catatan

WebSocket selalu diaktifkan saat menggunakan IIS Express.

Mengaktifkan WebSocket di IIS

Untuk mengaktifkan dukungan untuk protokol WebSocket pada Windows Server 2012 atau yang lebih baru:

Catatan

Langkah-langkah ini tidak diperlukan saat menggunakan IIS Express

  1. Gunakan wizard Tambahkan Peran dan Fitur dari menu Kelola atau tautan di Pengelola Server.
  2. Pilih Penginstalan berbasis peran atau Berbasis fitur. Pilih Selanjutnya.
  3. Pilih server yang sesuai (server lokal dipilih secara default). Pilih Selanjutnya.
  4. Perluas Server Web (IIS) di pohon Peran , perluas Server Web, lalu perluas Pengembangan Aplikasi.
  5. Pilih Protokol WebSocket. Pilih Selanjutnya.
  6. Jika fitur tambahan tidak diperlukan, pilih Berikutnya.
  7. Pilih Instal.
  8. Saat penginstalan selesai, pilih Tutup untuk keluar dari wizard.

Untuk mengaktifkan dukungan untuk protokol WebSocket pada Windows 8 atau yang lebih baru:

Catatan

Langkah-langkah ini tidak diperlukan saat menggunakan IIS Express

  1. Buka Panel Kontrol>Program>Program dan Fitur>Aktifkan atau nonaktifkan fitur Windows (sisi kiri layar).
  2. Buka simpul berikut: Layanan Informasi Internet> World Wide Web Services>Application Development Features.
  3. Pilih fitur Protokol WebSocket. Pilih OK.

Nonaktifkan WebSocket saat menggunakan socket.io di Node.js

Jika menggunakan dukungan WebSocket di socket.io pada Node.js, nonaktifkan modul IIS WebSocket default menggunakan webSocket elemen di web.config atau applicationHost.config. Jika langkah ini tidak dilakukan, modul IIS WebSocket mencoba menangani komunikasi WebSocket daripada Node.js dan aplikasi.

<system.webServer>
  <webSocket enabled="false" />
</system.webServer>

Aplikasi sampel

Aplikasi sampel yang menyertai artikel ini adalah aplikasi echo. Ini memiliki halaman web yang membuat koneksi WebSocket, dan server mengirim ulang pesan apa pun yang diterimanya kembali ke klien. Aplikasi sampel tidak dikonfigurasi untuk dijalankan dari Visual Studio dengan IIS Express, jadi jalankan aplikasi di shell perintah dengan dotnet run dan navigasikan di browser ke http://localhost:<port>. Halaman web menunjukkan status koneksi:

Status awal halaman web sebelum koneksi WebSockets

Pilih Koneksi untuk mengirim permintaan WebSocket ke URL yang ditampilkan. Masukkan pesan pengujian dan pilih Kirim. Setelah selesai, pilih Tutup Soket. Bagian Log Komunikasi melaporkan setiap tindakan terbuka, kirim, dan tutup saat terjadi.

Status akhir halaman web setelah koneksi WebSocket dan pesan pengujian dikirim dan diterima

Artikel ini menjelaskan cara memulai WebSocket di ASP.NET Core. WebSocket (RFC 6455) adalah protokol yang memungkinkan saluran komunikasi persisten dua arah melalui koneksi TCP. Ini digunakan dalam aplikasi yang mendapat manfaat dari komunikasi yang cepat dan real time, seperti obrolan, dasbor, dan aplikasi game.

Lihat atau unduh sampel kode (cara mengunduh). Cara menjalankan.

SignalR

ASP.NET Core SignalR adalah pustaka yang menyederhanakan penambahan fungsionalitas web real time ke aplikasi. Ini menggunakan WebSockets jika memungkinkan.

Untuk sebagian besar aplikasi, kami merekomendasikan SignalR lebih dari WebSocket mentah. SignalR menyediakan fallback transportasi untuk lingkungan di mana WebSocket tidak tersedia. Ini juga menyediakan model aplikasi panggilan prosedur jarak jauh dasar. Dan dalam sebagian besar skenario, SignalR tidak memiliki kerugian performa yang signifikan dibandingkan dengan menggunakan WebSocket mentah.

Untuk beberapa aplikasi, gRPC di .NET menyediakan alternatif untuk WebSockets.

Prasyarat

  • OS apa pun yang mendukung ASP.NET Core:
    • Windows 7 / Windows Server 2008 atau yang lebih baru
    • Linux
    • macOS
  • Jika aplikasi berjalan di Windows dengan IIS:
    • Windows 8 / Windows Server 2012 atau yang lebih baru
    • IIS 8 / IIS 8 Express
    • WebSocket harus diaktifkan. Lihat bagian dukungan IIS/IIS Express.
  • Jika aplikasi berjalan di HTTP.sys:
    • Windows 8 / Windows Server 2012 atau yang lebih baru
  • Untuk browser yang didukung, lihat Dapatkah saya menggunakan.

Mengonfigurasi middleware

Tambahkan middleware WebSockets dalam Configure metode Startup kelas:

app.UseWebSockets();

Catatan

Jika Anda ingin menerima permintaan WebSocket di pengontrol, panggilan ke app.UseWebSockets harus terjadi sebelum app.UseEndpoints.

Pengaturan berikut dapat dikonfigurasi:

  • KeepAliveInterval - Seberapa sering mengirim bingkai "ping" ke klien untuk memastikan proksi menjaga koneksi tetap terbuka. Defaultnya adalah dua menit.
  • AllowedOrigins - Daftar nilai header Asal yang diizinkan untuk permintaan WebSocket. Secara default, semua asal diizinkan. Lihat "Pembatasan asal WebSocket" di bawah ini untuk detailnya.
var webSocketOptions = new WebSocketOptions()
{
    KeepAliveInterval = TimeSpan.FromSeconds(120),
};

app.UseWebSockets(webSocketOptions);

Menerima permintaan WebSocket

Di suatu tempat nanti dalam siklus hidup permintaan (nanti dalam Configure metode atau dalam metode tindakan, misalnya) periksa apakah itu permintaan WebSocket dan terima permintaan WebSocket.

Contoh berikut adalah dari nanti dalam Configure metode :

app.Use(async (context, next) =>
{
    if (context.Request.Path == "/ws")
    {
        if (context.WebSockets.IsWebSocketRequest)
        {
            using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync())
            {
                await Echo(context, webSocket);
            }
        }
        else
        {
            context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
        }
    }
    else
    {
        await next();
    }

});

Permintaan WebSocket dapat masuk pada URL apa pun, tetapi kode sampel ini hanya menerima permintaan untuk /ws.

Pendekatan serupa dapat diambil dalam metode pengontrol:

public class WebSocketController : ControllerBase
{
    [HttpGet("/ws")]
    public async Task Get()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await Echo(webSocket);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }

Saat menggunakan WebSocket, Anda harus menjaga alur middleware tetap berjalan selama durasi koneksi. Jika Anda mencoba mengirim atau menerima pesan WebSocket setelah alur middleware berakhir, Anda mungkin mendapatkan pengecualian seperti berikut ini:

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.

Jika Anda menggunakan layanan latar belakang untuk menulis data ke WebSocket, pastikan Anda menjaga alur middleware tetap berjalan. Lakukan ini dengan menggunakan TaskCompletionSource<TResult>. Teruskan TaskCompletionSource ke layanan latar belakang Anda dan panggil TrySetResult saat Anda selesai dengan WebSocket. Task Kemudian await properti selama permintaan, seperti yang ditunjukkan dalam contoh berikut:

app.Use(async (context, next) =>
{
    using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync())
    {
        var socketFinishedTcs = new TaskCompletionSource<object>();

        BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);

        await socketFinishedTcs.Task;
    }
});

Pengecualian tertutup WebSocket juga dapat terjadi ketika mengembalikan terlalu cepat dari metode tindakan. Saat menerima soket dalam metode tindakan, tunggu kode yang menggunakan soket selesai sebelum kembali dari metode tindakan.

Jangan pernah menggunakan Task.Wait, Task.Result, atau panggilan pemblokiran serupa untuk menunggu soket selesai, karena dapat menyebabkan masalah utas yang serius. Selalu gunakan await.

Mengirim dan menerima pesan

Metode ini AcceptWebSocketAsync meningkatkan koneksi TCP ke koneksi WebSocket dan menyediakan WebSocket objek. WebSocket Gunakan objek untuk mengirim dan menerima pesan.

Kode yang ditampilkan sebelumnya yang menerima permintaan WebSocket meneruskan objek ke WebSocketEcho metode . Kode menerima pesan dan segera mengirim kembali pesan yang sama. Pesan dikirim dan diterima dalam perulangan hingga klien menutup koneksi:

private async Task Echo(HttpContext context, WebSocket webSocket)
{
    var buffer = new byte[1024 * 4];
    WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
    while (!result.CloseStatus.HasValue)
    {
        await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);

        result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
    }
    await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}

Saat menerima koneksi WebSocket sebelum memulai perulangan, alur middleware berakhir. Setelah menutup soket, alur melepas lelah. Artinya, permintaan berhenti bergerak maju dalam alur ketika WebSocket diterima. Ketika perulangan selesai dan soket ditutup, permintaan melanjutkan pencadangan alur.

Menangani pemutusan sambungan klien

Server tidak secara otomatis diberi tahu ketika klien terputus karena hilangnya konektivitas. Server menerima pesan pemutusan sambungan hanya jika klien mengirimkannya, yang tidak dapat dilakukan jika koneksi internet hilang. Jika Anda ingin mengambil beberapa tindakan ketika itu terjadi, atur batas waktu setelah tidak ada yang diterima dari klien dalam jendela waktu tertentu.

Jika klien tidak selalu mengirim pesan dan Anda tidak ingin kehabisan waktu hanya karena koneksi tidak aktif, minta klien menggunakan timer untuk mengirim pesan ping setiap X detik. Di server, jika pesan belum tiba dalam waktu 2*X detik setelah yang sebelumnya, hentikan koneksi dan laporkan bahwa klien terputus. Tunggu dua kali interval waktu yang diharapkan untuk menyisakan waktu tambahan untuk penundaan jaringan yang mungkin menahan pesan ping.

Catatan

Internal ManagedWebSocket menangani bingkai Ping/Pong secara implisit untuk menjaga koneksi tetap hidup jika KeepAliveInterval opsi lebih besar dari nol, yang defaultnya menjadi 30 detik (TimeSpan.FromSeconds(30)).

Pembatasan asal WebSocket

Perlindungan yang diberikan oleh CORS tidak berlaku untuk WebSocket. Browser tidak:

  • Lakukan permintaan pra-penerbangan CORS.
  • Hormati batasan yang ditentukan dalam Access-Control header saat membuat permintaan WebSocket.

Namun, browser mengirim Origin header saat mengeluarkan permintaan WebSocket. Aplikasi harus dikonfigurasi untuk memvalidasi header ini untuk memastikan bahwa hanya WebSocket yang berasal dari asal yang diharapkan yang diizinkan.

Jika Anda menghosting server Anda di "https://server.com" dan hosting klien Anda di "https://client.com", tambahkan "https://client.com" ke AllowedOrigins daftar untuk WebSockets untuk memverifikasi.

var webSocketOptions = new WebSocketOptions()
{
    KeepAliveInterval = TimeSpan.FromSeconds(120),
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");

app.UseWebSockets(webSocketOptions);

Catatan

Header Origin dikontrol oleh klien dan, seperti Referer header, dapat dipalsahkan. Jangan gunakan header ini sebagai mekanisme autentikasi.

Dukungan IIS/IIS Express

Windows Server 2012 atau yang lebih baru dan Windows 8 atau yang lebih baru dengan IIS/IIS Express 8 atau yang lebih baru memiliki dukungan untuk protokol WebSocket.

Catatan

WebSocket selalu diaktifkan saat menggunakan IIS Express.

Mengaktifkan WebSocket di IIS

Untuk mengaktifkan dukungan untuk protokol WebSocket pada Windows Server 2012 atau yang lebih baru:

Catatan

Langkah-langkah ini tidak diperlukan saat menggunakan IIS Express

  1. Gunakan wizard Tambahkan Peran dan Fitur dari menu Kelola atau tautan di Pengelola Server.
  2. Pilih Penginstalan berbasis peran atau Berbasis fitur. Pilih Selanjutnya.
  3. Pilih server yang sesuai (server lokal dipilih secara default). Pilih Selanjutnya.
  4. Perluas Server Web (IIS) di pohon Peran , perluas Server Web, lalu perluas Pengembangan Aplikasi.
  5. Pilih Protokol WebSocket. Pilih Selanjutnya.
  6. Jika fitur tambahan tidak diperlukan, pilih Berikutnya.
  7. Pilih Instal.
  8. Saat penginstalan selesai, pilih Tutup untuk keluar dari wizard.

Untuk mengaktifkan dukungan untuk protokol WebSocket pada Windows 8 atau yang lebih baru:

Catatan

Langkah-langkah ini tidak diperlukan saat menggunakan IIS Express

  1. Buka Panel Kontrol>Program>Program dan Fitur>Aktifkan atau nonaktifkan fitur Windows (sisi kiri layar).
  2. Buka simpul berikut: Layanan Informasi Internet> World Wide Web Services>Application Development Features.
  3. Pilih fitur Protokol WebSocket. Pilih OK.

Nonaktifkan WebSocket saat menggunakan socket.io di Node.js

Jika menggunakan dukungan WebSocket di socket.io pada Node.js, nonaktifkan modul IIS WebSocket default menggunakan webSocket elemen di web.config atau applicationHost.config. Jika langkah ini tidak dilakukan, modul IIS WebSocket mencoba menangani komunikasi WebSocket daripada Node.js dan aplikasi.

<system.webServer>
  <webSocket enabled="false" />
</system.webServer>

Aplikasi sampel

Aplikasi sampel yang menyertai artikel ini adalah aplikasi echo. Ini memiliki halaman web yang membuat koneksi WebSocket, dan server mengirim ulang pesan apa pun yang diterimanya kembali ke klien. Aplikasi sampel tidak dikonfigurasi untuk dijalankan dari Visual Studio dengan IIS Express, jadi jalankan aplikasi di shell perintah dengan dotnet run dan navigasikan di browser ke http://localhost:5000. Halaman web menunjukkan status koneksi:

Status awal halaman web sebelum koneksi WebSockets

Pilih Koneksi untuk mengirim permintaan WebSocket ke URL yang ditampilkan. Masukkan pesan pengujian dan pilih Kirim. Setelah selesai, pilih Tutup Soket. Bagian Log Komunikasi melaporkan setiap tindakan terbuka, kirim, dan tutup saat terjadi.

Status akhir halaman web setelah koneksi WebSocket dan pesan pengujian dikirim dan diterima