مقدمة: لماذا WebSocket على Rust هو ثنائي قوي
تتطلب تطبيقات الويب الحديثة بشكل متزايد نقل البيانات الفوري: الدردشات، الألعاب عبر الإنترنت، أسعار الأسهم، الإشعارات. بروتوكول HTTP التقليدي بنموذج الطلب والاستجابة غير فعال هنا - فهو يخلق حمولة إضافية وتأخيرات. يأتي بروتوكول WebSocket للمساعدة، والذي ينشئ اتصالاً ثنائي الاتجاه دائم بين العميل والخادم.
لغة Rust مع تركيزها على الأداء، أمان الذاكرة، وتكلفة الصفر للتجريدات مثالية لبناء أنظمة real-time عالية التحميل. في هذه المقالة، سنشرح كيفية بناء خادم WebSocket باستخدام Rust ومكتبة tokio-tungstenite، وسنستعرض الأنماط الرئيسية لتطبيقات real-time.
أساسيات WebSocket وإعداد البيئة
ما هو WebSocket؟
WebSocket هو بروتوكول يعمل فوق TCP. يسمح للخادم والعميل بتبادل الرسائل في الوقت الفعلي دون الحاجة إلى مصافحات HTTP متكررة. يبدأ الاتصال كطلب HTTP مع ترقية إلى WebSocket، وبعد ذلك يتم نقل البيانات في إطارات (frames).
تثبيت التبعيات
للعمل، سنحتاج إلى tokio (بيئة تشغيل غير متزامنة) و tokio-tungstenite (تنفيذ WebSocket). أنشئ مشروعاً جديداً:
cargo new websocket_democd websocket_demoأضف التبعيات في Cargo.toml:
[dependencies]tokio = { version = "1", features = ["full"] }tokio-tungstenite = "0.21"futures-util = "0.3"إنشاء خادم WebSocket بسيط
خادم الصدى الأساسي
لنبدأ بخادم بسيط يقبل الاتصالات ويعيد إرسال الرسائل المستلمة (وضع الصدى). هذا هو "Hello, World" الكلاسيكي لـ WebSocket.
use tokio::net::TcpListener;use tokio_tungstenite::accept_async;use tokio_tungstenite::tungstenite::Message;use futures_util::StreamExt;
#[tokio::main]async fn main() { let addr = "127.0.0.1:8080"; let listener = TcpListener::bind(addr).await.expect("فشل ربط العنوان"); println!("الخادم قيد التشغيل على {}", addr);
while let Ok((stream, peer)) = listener.accept().await { println!("اتصال جديد: {}", peer); tokio::spawn(handle_connection(stream)); }}
async fn handle_connection(stream: tokio::net::TcpStream) { let ws_stream = accept_async(stream) .await .expect("خطأ أثناء مصافحة WebSocket");
let (_, mut read) = ws_stream.split();
while let Some(msg) = read.next().await { match msg { Ok(Message::Text(text)) => { println!("تم الاستلام: {}", text); // هنا يمكن إرسال رد، لكن للتبسيط سنتجاوز } Ok(Message::Close(_)) => break, _ => {} } }}هذا الكود يشغل خادم TCP على المنفذ 8080. لكل اتصال جديد، يتم إنشاء مهمة غير متزامنة handle_connection تقوم بمصافحة WebSocket وقراءة الرسائل الواردة.
إضافة إرسال الرسائل
لجعل خادم الصدى كاملاً، نحتاج إلى تقسيم التدفق إلى قراءة وكتابة. نستخدم futures_util::StreamExt و SinkExt:
use futures_util::SinkExt;
async fn handle_connection(stream: tokio::net::TcpStream) { let ws_stream = accept_async(stream) .await .expect("خطأ أثناء مصافحة WebSocket");
let (mut write, mut read) = ws_stream.split();
while let Some(msg) = read.next().await { match msg { Ok(Message::Text(text)) => { let echo = format!("صدى: {}", text); write.send(Message::Text(echo.into())).await.unwrap(); } Ok(Message::Close(_)) => break, _