برنامه نويسي نرم افزارهاي تحت شبکه

از آنجايي که نياز به برنامه هاي تحت شبکه براي برقراري ارتباط کاربران با يکديگر هر روز بيشتر مي شود، لذا تأثير بسزاي سيستم هاي مربوطه، در بالا بردن بازده کاري، بر کسي پوشيده نيست. در اين مقاله، نوع خاصي از برنامه مورد توجه قرار نگرفته و مفاهيم کلي هر نرم افزار تحت شبکه، مورد بحث است. اگر چه بايد توجه داشته باشيد که اين مقاله صرفاً براي برنامه نويسان تهيه شده و از کدها و اصطلاحات برنامه نويسي نيز در آن استفاده گرديده است. مسلماً براي پياده سازي اينگونه
يکشنبه، 8 شهريور 1388
تخمین زمان مطالعه:
موارد بیشتر برای شما
برنامه نويسي نرم افزارهاي تحت شبکه
برنامه نويسي نرم افزارهاي تحت شبکه
برنامه نويسي نرم افزارهاي تحت شبکه

نويسنده:حامد حبيبي



بررسي مباحث تئوري و عملي براي برنامه نويسان حيطه شبکه

مقدمه

از آنجايي که نياز به برنامه هاي تحت شبکه براي برقراري ارتباط کاربران با يکديگر هر روز بيشتر مي شود، لذا تأثير بسزاي سيستم هاي مربوطه، در بالا بردن بازده کاري، بر کسي پوشيده نيست. در اين مقاله، نوع خاصي از برنامه مورد توجه قرار نگرفته و مفاهيم کلي هر نرم افزار تحت شبکه، مورد بحث است. اگر چه بايد توجه داشته باشيد که اين مقاله صرفاً براي برنامه نويسان تهيه شده و از کدها و اصطلاحات برنامه نويسي نيز در آن استفاده گرديده است. مسلماً براي پياده سازي اينگونه برنامه ها مي بايست از يک زبان برنامه نويسي توانمند استفاده نمود. زبان برنامه نويسي Cبا استفاده از توابع کتابخانه اي موجود در فايلsocket.h، و همچنين زبان هايي مانند دلفي و ويژاوال بيسيک، امکان ايجاد برنامه هاي تحت شبکه را از طريق واسط winsockدر سيستم عامل ويندوز برايتان فراهم مي کند. در طرف ديگر، تکنولوژيNet.شرکت مايکروسافت، تحول عظيمي در زبان هاي برنامه نويسي به وجود آورده و بخشي از اين تحولات، مربوط به کلاس هاي برنامه نويسي تحت شبکه در اين چارچوب به شمار مي آيد. لازم به اشاره است که شرکت مايکروسافت در نسخه Net Framework 2.0، تعدادي از متدهاي مربوط به برنامه نويسي تحت شبکه را به صورت متروک در آورده است (منظور متدي است که در استاندارد بعدي زبان، به يک متد منسوخ تبديل شده و از آن حذف مي شود) که در اين مقاله تعدادي از اين متدها بررسي شده اند. همچنين به علت محبوبيتي که زبان #VB.Net,Cبين برنامه نويسان پيدا کرده اند، در اين مقاله روش هاي برنامه نويسي تحت شبکه، با استفاده از اين زبان هاي مبتني بر Net بررسي شده است. ضمناً اضافه مي شود که پس از خواندن اين مقاله، با مفاهيم زير آشنا خواهيد شد:
*استريم ها
*مفاهيم کلي TCP
*نحوه يافتن اطلاعات آدرس IP
*مفهوم سوکت در ارتباطات شبکه اي
*تعريف برنامه سرويس گيرنده(client) و سرويس دهنده (Server)
*برنامه نويسي تحت شبکه براي سوکت هاي اتصال گرا (TCP)
*مشکلات ارتباط TCPو مقابله با آنها

استريم

همانطور که مي دانيد، روش هاي بسياري براي ذخيره سازي اطلاعات در حافظه، فايل، ابزارهاي ورودي/خروجي، خطوط ارتباط داخلي و خطوط ارتباطي شبکه وجود دارند. لازم به ذکر است که معمولاً داده ها به صورت بايت به بايت نوشته و خوانده مي شوند که در مقابل ضريب اطمينان بالايي که اين روش دارد، کارايي آن قابل توجه نيست. به همين جهت براي بالا بردن کارايي، مي توان از روشي که استريم ها از آن استفاده مي کنند، استفاده کرده و در هر لحظه، به جاي خواندن و نوشتن بايت به بايت، بر روي بلوکي از داده ها کار کرده و چندين بايت را مبادله نمود. در کار با فايل ها و برخي ابزارهاي ورودي/خروجي و ارتباطات شبکه اي مي توان از استريم ها براي بالا بردن سرعت عملکرد برنامه ها استفاده کرد. واضح است که براي ابزارهايي همچون CD-ROM ، استريم، عمل "نوشتن" را انجام نمي دهد. همچنين در خطوط ارتباطي شبکه نيز استريم ها از عمل جستجو، پشتيباني نمي کنند.

خصوصيات

مهمترين خصوصيتTCP، اتصال گرا بودن آن و بدان معناست که تنها در صورتي دو کامپيوتر با يکديگر ارتباط دارند که يک کانال اتصال با يکديگر بر قرار کرده باشند (در بخش هاي بعدي با اين خصوصيت، بيشتر آشنا خواهيدشد). در اين روش امکان مبادله داده بدون برقراري اتصال وجود نداشته باشد، استريمي جهت اطمينان از مبادله صحيح و دقيق داده ها ايجاد مي شود.
در نظر داشته باشيد که صحت داده ها TCP را تضمين مي کند، اما عدم ذخيره کردن محدوده هاي پيغام (ابتدا و انتهاي پيغام)در اين نوع ارتباط باعث بروز مشکلاتي مي شود که در ادامه مقاله به بررسي برخي از آنها و راه حل هاي مقابله خواهيم پرداخت.
پس از پذيرفته شدن داده (Data1)براي ارسال، TCP مدتي آن را در بافر خود نگه داشته و حتي در صورت ارسال داده ديگري توسط برنامه شما، مجدداً در کنار اولين داده نگهداري مي گردد. همان طور که پيشتر گفته شد، استريم ها بلوکي از داده ها را ارسال کرده و به همين جهت داده از بافر، بصورت يک بسته ارسال خواهد شد (Data1وData2). در سمت ديگر، ماشين دريافت کننده داده ها تنها يک بسته اطلاعاتي دريافت مي کند و چون محدوده هاي پيغام (ابتدا و انتهاي پيغام) در TCP ذخيره نمي شود، ماشين دريافت کننده، بسته هاي دريافتي را تنها بصورت يک پيغام خواهد ديد (Data2+Data1).

يافتن اطلاعات آدرس IP

براي يافتن آدرسIP، چهار روش زير وجود دارد که در اين بخش تنها به روش برنامه نويسي از طريق DNSاشاره خواهيم کرد.
*دستور IPConfig
*استفاده از رجيستري ويندوز
*استفاده از پايگاه داده WMI
*استفاده از Domain Name System) DNS)
کتابخانهNet. در فضاي نام System.Net، کلاس DNS را تدارک ديده و از ميان متدهاي مختلفي که اين کلاس در اختيار برنامه نويسان قرار داده است، دو متد براي يافتن آدرس IPسيستم محلي کاربرد دارند. متد() GetHostname، نام ميزبان را بر مي گرداند و متد () GetHostByName(در نسخه از NET 2.0متد () GetHostEntry به جاي اين متد مي توانيد استفاده کنيد) آدرس IP ميزبان مشخص شده را استخراج مي کند. با ترکيب اين دو متد، مي توان آدرسIPسيستمي را بدست آورد. براي درک بهتر طريقه استفاده از اين متدها به تکه برنامه زير توجه نماييد:

using System
using System.Text
using System.Net
using System.Net.Sockets
namespace TestSocket
{
class Program {
static void Main(string[]args) {
try {
IPHostEntry IPHost=Dns.GetHostEntry("www.hotmail.com")
IPAddress[]ipAddress=IPHost.AddressList
StringBuilder strIpAddress=new StringBuilder()
for (int i=0,i<ipAddress.Length,i++) {
strIpAddress.Append(ipAddress[i].ToString()),
}
Console.WriteLine("IP is:"+strIpAddress.ToString())
}
catch (SocketException ex) {
Console.WriteLine("Error Occured! "+ex)
}
Console.Read()
} } }

همچنين از متد()Resolve نيز مي توان براي اين منظور استفاده کرد.

سوکت

سوکت به عنوان يک رابط و واسط ميان برنامه و پروتوکل شبکه اي مانند TCP، مهمترين بخش از يک برنامه تحت شبکه محسوب مي شود. براي درک بهتر عملکرد سوکت، به بررسي مثالي مي پردازيم. فرض کنيد که يک برنامه نويس، برنامه اي را نوشته که مقاديري را در فايلي درون هارد ديسک ذخيره مي کند. واقعيت امر آن است که برنامه نويس تنها از طريق متدهاي موجود، اقدام به اين عمل کرده و هيچ اطلاعاتي از عملکرد فيزيکي پشت صحنه که سيستم عامل ندارد. بطور مشابه در مورد سوکت نيز، برنامه نويسان بدون توجه به جزئيات مربوط به کارت هاي شبکه نصب شده بر روي سيستم، نحوه ايجاد بسته هاي اطلاعاتي و موارد مشابه ديگر، با ايجاد سوکت و فراخواني متدهايي از آن عمليات مربوط به برقراري اتصال، ارسال، دريافت بسته هاي اطلاعاتي و پايان دادن به ارتباط را انجام داده و اعمال سطح پايين تر، توسط سيستم عامل انجام مي پذيرد.
برنامه هاي سرويس دهنده و سرويس گيرنده

برنامه سرويس دهنده (Server)

برنامه سرويس دهنده برنامه اي است که بر روي سرور شبکه نصب شده و در خواست هايي را دريافت و پس از پردازش، پاسخ مناسبي به سرويس گيرنده ارسال مي کند. در حالت کلي مي توان اينگونه نتيجه گرفت که برنامه سمت سرور نمي تواند آغاز کننده ارتباط باشد.

برنامه سرويس گيرنده (Client)

برنامه سرويس گيرنده بر روي Client قرار گرفته و درخواست هايي را به ماشين سرور ارسال مي کند و سپس منتظر دريافت پاسخ مي ماند. لازم به اشاره است که برنامه سرويس گيرنده را مي توان ماشين آغاز کننده ارتباط عنوان کرد زيرا شروع کننده درخواست است.

نحوه مديريت آدرس هاي IP در Net.

بر برقراري اتصال بين دو ماشين و اختصاص دادن آدرسIPکارت شبکه خاص به يک سوکت، مي بايست يک آدرس IP و شماره پورت (در مدت ارتباط از اين پورت براي تبادل اطلاعات استفاده خواهد شد) مشخص نماييد. کتابخانهNet. براي مديريت مقدار آدرس IPو پورت، دو کلاس IPAddress و IPEndPoint از فضاي نام System.Net را در اختيارتان قرار داده است.

کلاس IPAddress

از اين کلاس مي توان در متدهاي سوکت و براي نشان دادن آدرس IPکارت شکبه مورد نظر استفاده نمود. روش تعريف اين object به شکل زير است:
System.Net.IPAddress ip=System.Net.IPAddress.Parse(string ipString)
پارامتر ipStringدر ساختار فوق، آدرسIP کارت شبکه را به صورت رشته اي مشخص مي کند. حال پس از تعريف يک Instanceاز اين شئي، مي توانيد به خصوصيات آن دست يابيد. در عين حال علاوه بر اين روش، درNet frame work 2.0 نيز مي توانيد از متد زير استفاده کنيد:
byte[]arrIp={192,168.0.1}
System.Net.IPAddress ip=new System.Net.IPAddress(arrIp)

کلاس IPEndPoint

از اين کلاس به منظور نشان دادن ترکيب آدرس IPو شماره پورت استفاده مي شود. هنگامي که بخواهيد سوکتي را به آدرسIP بخصوصي مقيد کرده يا بين سوکت يا آدرس IP ديگري ارتباط برقرار کنيد، نمونه اي از اين شي ايجاد کرده و از آن در فراخواني ها استفاده مي کنيد. فرم کلي استفاده از اين کلاس به شکل زير است:
IPEndPoint(IPAddress address,int port)
همان طور که ملاحظه مي کنيد، پارامتر اول، نمونه اي از شي و پارامتر دوم شماره پورت اتصال را مشخص مي کند.
توجه:
از آنجا که شماره هاي کمتر از 1024توسط سيستم عامل مورد استفاده قرار مي گيرد، بنابراين از شماره پورت هاي بالاتر از 1024استفاده کنيد.

نحوه استفاده از سوکت ها

در اين زمينه، فضاي نام System.Net.Sockets ،کلاس Socketرا براي استفاده در برنامه هاي تحت شبکه در اختيار برنامه نويسان قرار داده است. که سازنده کلاس Socket به شکل زير است:
public Socket (AddressFamily addressFamily,Socketype Socketype,ProtocolType ProtocolType)
پارامتراول در فرم فوق، "نوع شبکه" و پارامتر دوم "نوع اتصال" و پارامتر سوم "پروتکل ارتباطي" را تعيين کرده که تمامي پارامترهاي اين ساختار از نوع شمارشي (عددي) مي باشند. بايد توجه داشته باشيد که به هيچ عنوان اجازه ترکيب پارامترهاي دوم و سوم را نداشته و براي هر SocketType، مي بايست از يک ProtocolType ويژه استفاده کنيد. ضمناً فراموش نکنيد که براي استفاده ProtocolType با مقدارStream، مقدار ProtocolType را به Tcp تنظيم نماييد.
پس از نشان دادن هر يک از قسمت هاي اصلي تشکيل دهنده يک برنامه تحت شبکه، حال به بررسي نحوه ايجاد برنامه هاي تحت شبکه مي پردازيم.

انتخاب نوع سوکت ارتباطي

اساساً سوکت ها را به دو بخش اتصال گرا (Connection-Oriented) و بدون اتصال (Connection-Less)مي توان تقسيم بندي کرد.
در سوکت هاي اتصال گرا مانند TCP، براي مبادله داده ها بين دو ماشين، حتماً بايد پيشتر، اتصالي برقرار شده باشد. اما در سوکت هاي بدون اتصال مانندUDP، نيازي به برقراري اتصال نبوده و در عوض، به ازاي هر بار ارسال داده به ماشين ديگر، آدرسIP ماشين مقصد مي بايست مشخص شود. از آنجايي که سوکت هاي اتصال گرا از محبوبيت بيشتري در ايجاد برنامه هاي تحت شبکه برخوردار هستند، لذا در اين بخش مقاله فقط به بررسي اين نوع سوکت ها پرداخته ايم.

سوکت اتصال گرا (Connection-Oriented)

در اين بخش مقاله، مراحل مختلف ايجاد برنامه هاي سرويس دهنده و سرويس گيرنده اي که از TPCبراي ارتباط استفاده مي کنند، را بررسي کرده و همچنين متدهاي مختلف کلاسsocket که براي برقراري ارتباط و همچنين مبادله داده ها وجود دارند، شرح خواهيم داد. براي شروع، ابتدا برنامه سرويس دهنده ساده اي ايجاد خواهيم کرد، سپس به بررسي برنامه سرويس گيرنده مي پردازيم.

عمليات برنامه سرويس دهنده (سرور)

مراحل ايجاد برنامه سرويس دهنده به عبارتند از:
*ايجاد سوکت
*مقيد کردن(تخصيص)سوکت به يک کارت شبکه (آدرسIP معين)
*گوش دادن به درخواست ها
*پذيرش درخواست ها براي برقراري اتصال
*مبادله داده ها
*پايان دا دن به ارتباط و بستن سوکت
در مرحله اول پس از ايجاد نمونه اي از شيsocket، براي مقيد کردن سوکت به يک آدرسIP خاص، بايد متد()Bindرا فراخواني کنيد:
IPAddress ipaddr=IPAdress.Parse("192.168.0.1")
IPEndPoint iep=new IPEndPoint(ipaddr,9050)
socket.Bind(iep)
پارامترiep نمونه اي از شيIPEndPoint مي باشد که در مرحله دوم، اختصاص شي socket توسط آدرسIP و شماره پورت مشخص شده در آن، انجام مي شود. گام بعدي، گوش دادن به درخواست هاي ورودي است که براي اين منظور، متد()Listen را فراخواني کنيد:
(5)socket.Listen
ضمناً پارامتر backlog، مشخص کننده تعداد درخواست هاي اتصالي است که مي توانند در صف قرار گيرند تا در زمان مناسب به درخواست آنها رسيدگي شود. هر در خواستي که بيش از اين تعداد باشد، ناديده گرفته خواهد شد. هراندازه مقدار اين پارامتر بزرگ باشد، به همان اندازه فضاي کمتري براي ارسال و دريافت بسته ها خواهيد داشت.سپس با فراخواني متد()Accept به درخواست اتصال ورودي پاسخ داده و توسط سوکت جديدي که اين متد بر مي گرداند، در مرحله چهارم مي توانيد با سرويس گيرنده مبادله داده ها را آغاز کنيد. همچنين متدهاي()Sendو()Receive به ترتيب براي ارسال و دريافت بسته هاي TCP بکار مي روند. هر يک از اين متدها به چهار شکل مختلف سربار گذاري شده اند که جدول 1-1، آنها را به زبان#C نشان مي دهد.

جدول1-1:متدهای Send() , Receive

متد

توضیحات

Receive (byte [] data)

داده ها را دریافت و در آرایه بایتی قرار می دهد

Receive (byte [] data, Socket Flags sf)

صفات مربوط به سوکت را تنظیم کرده، داده ها را دریافت می کند و آن ار در آرایه بایتی قرار می دهد.

Receive (byte [] data, int size, Socket Flags sf)

صفات مربوط به سوکت را تنظیم کرده، سایز مشخصی از داده را دریافت می کند و آن را در آرایه بایتی قرار می دهد.

Receive (byte [] data, int offset, int size, Socket Flags sf)

صفات مربوط به سوکت را تنظیم کرده، سایز مشخصی از داده را دریافت می کند و آن را از offset،در آرایه بایتی قرار می هد.

Send (byte [] data)

داده ها را دریافت و در آرایه بایتی را ارسال می کند.

Receive (byte [] data, Socket Flags sf)

صفات مربوط به سوکت را تنظیم کرده، داده ها را دریافت می کند و آن ار در آرایه بایتی ارسال می کند.

Send (byte [] data, int size, Socket Flags sf)

صفات مربوط به سوکت را تنظیم کرده، سایز مشخصی از داده را دریافت می کند و آن را در آرایه بایتی را ارسال می کند.

Send (byte [] data, int offset,, int size, Socket Flags sf)

صفات مربوط به سوکت را تنظیم کرده، سایز مشخصی از داده قرار گرفته در آرایه بایتی با شروع از افست offset،ارسال می هد.


در نهايت براي خاتمه دادن به ارتباط، دو متد ()Shutdown و ()Closeدر اختيار شما قرار دارند. متد()Close، پس از فراخوانيه ي سريعاً سوکت را بسته و منابع تخصيص يافته را آزاد مي کند. اما متد()Shutdown، پارامتري دريافت مي کند که اين پارامتر مشخص کننده نحوه بستن سوکت است. مقادير مجاز براي اين پارامتر در جدول 2-1آمده است.

جدول 2-1:مقادیر SocketShutdown

مقدار

توضیحات

SocketShutdown.Both

از ارسال و دریافت داده ممانعت می کند.

SocketShutdown. Receive

از دریافت داده ممانعت کرده و اگر داده دیگری دریافت شود، یک RSTارسال می­شود.

SocketShutdown. Send

از ارسال داده بر روی سوکت جلوگیری کرده و پس از ارسال همه داده بافر، یک FINارسال می شود.

عمليات برنامه سرويس گيرنده

*ايجاد سوکت
*متصل شدن به سرورها
*ارسال و دريافت داده ها
*پايان دادن به ارتباط و بستن سوکت
همانند برنامه سرور، ايجاد سوکت در برنامه سرويس گيرنده(Client)جزو نخستين مرحله ايجاد نمونه اي از شي Socket است. پس از انجام اين عمل، حال مي بايست متد ()Connect را براي ارسال درخواست اتصال، فراخواني نماييد. توجه داشته باشيد که اين متد براي متصل شدن به سرويس دهنده، به آدرس IP مقصد و شماره پورتي که اتصال بر روي آن انجام خواهد پذيرفت، نياز دارد که توسط پارامتري از نوع مشخص IPEndPoint مي گردد.
socket.()Connect(IPAddress.Parse("192.168.0.1",9050))
توجه: اين امکان وجود دارد که برنامه سرويس دهنده به هنگام ارسال درخواست اتصال توسط سرويس گيرنده، به درخواست هاي ورودي گوش نکرده و با خطايي مواجه گردد که براي رفع اين مشکل احتمالي، بهتر است متد()Connect را داخل بلوکtry-Catch بنويسيد.
توجه داشته باشيد که به هنگام بروز خطا در هر يک از متدهاي Socket، استثنايي از نوع SocketException رخ خواهد داد. در ضمن به علت اينکه پيغام ها بايد به صورت آرايه هاي بايتي مبادله شوند، بنابراين مي بايست داده ها را به آرايه هاي بايتي تبديل کرده و در سمت دريافت کننده، مجدداً آرايه بايتي به پيغام مناسب تبديل شود.براي تبديل رشته ها به آرايه هاي بايتي و بالعکس، مي توانيد از کلاس Encoding واقع در فضاي نام System.Text استفاده نماييد. اگر رشته مورد نظر فقط شامل کاراکترهاي اسکي باشد، با استفاده از خصوصيت ASCII و متدهاي ()GetString و ()GetBytes ، رشته ها را به آرايه هاي بايتي و آرايه هاي بايتي را به رشته تبديل کنيد.
byte buffer=System.Text.Encoding.ASCII.GetBytes(Sample Text")
string str =System .Text.Encoding.ASCII.GetString (buffer)
نکته: اگر رشته ارسالي شامل کاراکترهاي فارسي است، در صورت استفاده از خصوصيت ASCII براي تبديل رشته به آرايه بايتي، با نتايج ناصحيح در ماشين دريافت کننده مواجه خواهيد شد (بجاي کاراکترهاي فارسي، "؟" قرار مي گيرد). براي تبديل رشته هايي که در آن از کاراکترهاي فارسي استفاده شده است، از خصوصيت UTF-8 و متدهاي ()GetString و ()GetBytes آن استفاده کنيد.
byte buffer=System.Text.Encoding.UTF8.GetBytes("متن فارسي")
string str=System.Text.Encoding.UTF8.GetString (buffer)

مشکلات TCP

در برنامه هاي تحت شبکه اي که از پروتکل TCP در ارتباط خود با ماشين ديگر استفاده مي کنند، برنامه نويسان با دو مشکل اساسي زير مواجه هستند:
*استفاده نامناسب و دستکاري ناصحيح بافر داده
*اندازه نامناسب پيغام ها در شبکه
دقت نظر داشته باشيد که در برنامه هايي که ذکر گرديد، همه پيغام ها قالب ثابت و مشخصي داشته و اندازه آنها کنترل شده بود، اما در اصل هنگام برقراري ارتباط سرويس دهنده با سرويس گيرنده، هيچ يکي از آنها شناختي در مورد طول يا نوع داده هاي ارسالي صرف مقابل ندارند. حال اين سوال مطرح مي شود که هنگام دريافت اطلاعاتي که حجمشان بيش از حجم تعيين شده است، چه بايد کرد؟
خب استفاده از بافر داده بزرگ يا بافر داده کوچک هر يک مزايا و معايب خود را دارند که بررسي آنها خارج از حوصله اين مقاله است ولي به طور خلاصه بدانيد که بر حسب عملکرد برنامه، مي بايست سايز بافر داده را تعيين کنيم. ضمناً مشکل ديگر ارتباط TCP، عدم نگهداري محدوده هاي پيغام در اين نوع ارتباط است که براي بر طرف کردن آن سه روش وجود دارد:
*ارسال پيغام با طول ثابت
*ارسال اندازه پيغام به همراه پيغام
*استفاده از سيستم نشانه گذاري براي جدا کردن پيغام ها
حال از آنجايي که روش سوم از سه روش ياد شده، کاراتر بوده و ضريب اطمينان بالاتري دارد، در ادامه به بررسي آن خواهيم پرداخت.

ارسال اندازه پيغام به همراه خود پيغام

در اين روش ،طول پيغام ،متغير بوده و همين خصوصيت باعث افزايش کارايي آن شده است. اما تنها مشکلي که در اين روش وجود دارد، آگاه کردن ماشين دريافت کننده از طول پيغام است که اين مشکل را مي توان با ارسال همزمان اندازه و پيغام، رفع نمود. بطور کلي روش هاي مختلفي براي ارسال اندازه پيغام وجود دارد که ساده ترين اين روش ها، ايجاد متني با قرار گرفتن طول پيغام در آن است. سپس اين متن را به ابتداي پيغام اضافه کرده و ارسال مي کنيم.براي استفاده از اين روش، هر دو برنامه سرويس دهنده وسرويس گيرنده بايد از تعداد بايت هاي بکار رفته جهت مشخص کردن طول پيغام، آگاه باشند و مسلماً بکار بردن تنها يک بايت (يک کاراکتر)، براي نشان دادن طول پيغام کافي نخواهد بود. اگر چه استفاده از 3 يا 4 بايت نيز مي تواند بهتر باشد، اما در پيغام هاي کوچکتر باعث اتلاف حافظه مي گردد.
همچنين راه حل ديگري به منظور ارسال مقدار عددي اندازه پيغام وجود دارد که برطبق آن، مقدار صحيح عددي را به يک آرايه بايتي تبديل کرده و قبل از ارسال پيغام، آن را بفرستيد. براي تبديل مقدار صحيح عددي به آرايه عددي به آرايه بايتي، از متد()GetBytes کلاس BitConverter که در فضاي نام System قرار دارد، استفاده شده است. ضمناً در سمت ماشين دريافت کننده نيز بايد ابتدا اندازه پيغام دريافت شده و سپس خود پيغام، با توجه به اندازه دريافتي، دريافت گردد.
اخطار:
متدهاي کلاس BitConverter ، بسته به ماشيني که بر روي آن اجرا مي شوند، عمل تبديل را انجام داده و اگر هر دو ماشين (سرويس دهنده و سرويس گيرنده) از سيستم عامل ويندوز به همراه پردازنده اينتل به همراه پردازنده AMDاستفاده کنند، نتيجه درست خواهد بود. اما در صورتي که يکي از آنها از نوع ديگري باشد، امکان بروز نتايج نادرست وجود دارد. البته براي رفع اين مشکل نيز راه حلي وجود دارد که با تبديل کردن ترتيب بايت هاي ارسالي به ترتيب بايت شبکه انجام مي گيرد. لازم به ذکر است که در اين مورد نيزNet.دو متد استاتيک در کلاس IPAddress به نام هاي()HostToNetworkByteOrder و() NetworkToHostByteOrder در اختيار برنامه نويسان قرار داده است.

استفاده از سيستم نشانه گذاري

دراين روش براي جداسازي پيغام ها، از کاراکترهاي جدا کننده ويژه اي بين دو پيغام استفاده مي شود. بايد در نظر داشت که مشکل اين روش، کارايي پائين آن است. در صورتيکه بخواهيم در خود پيغام، از اين کاراکتر(هاي) جدا کننده استفاده کنيم، برنامه دريافت کننده با رسيدن به اين کاراکتر، آن را به عنوان جدا کننده استفاده کنيم، برنامه دريافت کننده با رسيدن به اين کاراکتر، آن را به عنوان جدا کننده پيغام فرض کرده و در نتيجه، پيغام بدست آمده نادرست خواهد بود. در اين رابطه، کتابخانهNet براي تأمين اهداف برنامه نويسان، يک نوع استريم با استفاده از کلاس هاي StreamReader و StreamWriter فراهم آورده که توسط کاراکتر ويژه اي، پيغامتان را نشانه گذاري مي کند. نمونه برنامه استفاده از اين کلاس ها، بصورت برنامه هاي سرويس دهنده و سرويس گيرنده، در اين قسمت قرار گرفته تا علاوه با آشنايي شما با استفاده از اين دو کلاس، از مشاهده برنامه Client/Server نيز بي بهره نمانيد.
مثال زير نمونه ساده اي از يک برنامه سرور است:

using System
using System.Text
using System.Net
using System.Net.Sockets
class Socketserver {
public static void Main() {
StreamWriter StreamWriter
StreamReader StreamReader
NetworkStream NetworkStream
tcpListener tcpListener=new tcpListener(5555)
tcpListener.Start()
Console.WriteLine("The Server has started on port5555")
Socket serverSocket=tcpListener.AccptSocket()
try {
if(serverSocket.Connected) {
while (true) {
Console.WriteLine("Client connected")
NetworkStream=new NeworkStream(serverSocket)
StreamWriter=new streamWriter(networkStream)
StreamReader=new streamReader(networkStream)
Console.WriteLine(StreamReader.ReaderLine())
}
}
if(serverSocket.Connected)
serverSocket.Close()
Consol.Read()
}
catch(SocketException ex) {
Console.WriteLine(ex)
} } }
مثال زير نمونه ساده اي از يک برنامه سرويس گيرنده (Client) است:
using System
using System.Text
using System.Net
using System.Net.Sockets
class SocketClient {
static void Main(string[]args) {
TcpClient tcpClient
NetworkStream networkStream
StreamReader streamReader
StreamWriter streamWriter
try {
TcpClient=new TcpClient("localhost",5555)
NetworkStream=TcpClient.GetStream()
StreamReader=new streamReader(networkStream)
StreamWriter=new streamWriter(networkStream)
StreamWriter.WriteLine("Message from the Client...")
StreamWriter.Flush() }
catch(SocketException ex) {
Console.WriteLine(ex)
}
Consol.Read()
} }

توجه:همواره سعي کنيد که برنامه هاي تحت شبکه را حتماً بر روي يک شبکه واقعي امتحان کنيد. زيرا در صورتي که برنامه سرويس گيرنده و سرويس دهنده، هر دو بر روي يک ماشين اجرا شوند، برنامه ها از بيشتر خطاهاي رايج مربوط به يک محيط شبکه، دور خواهند بود.

سوکت هاي بلوکه کننده

متدهاي ورودي/خروجي کلاسSocket، باعث بلوکه شدن اجراي برنامه مي گردند. بدين منظور که هنگام رسيدن برنامه به هر يک از اين متدها، روند اجرايي برنامه تا کامل شدن آنها بلوکه خواهد شد. براي مثال فرا خواني متد()Receive تا زماني که داده اي بر روي سوکت نمايان نشود، اجراي برنامه را بلوکه مي کند. چهار روش مختلف براي جلوگيري از بلوکه شدن در فراخواني متدها وجود دارد که عبارتند از:
*استفاده از سوکت هاي non-block
*استفاده از سوکت هاي Multiplex
*استفاده از سوکت هاي آسنکرون (Asynchronous)
*چند نخي کردن (Multithreading)
همچنين مفاهيم پيشرفته تري که در زير آمده، مي تواند زمينه اي براي مطالعه بيشتر خوانندگان باشد.
*بکارگيري سوکت هاي بدون اتصال (uDP)
*مشکلات uDPو مقابله با آنها
*استفاده از سوکت هاي non-block
*استفاده از سوکت هاي multiplex
*کلاس هاي TCPListener،
، TCPClient و UDPClient
*نحوه ارسال داده هاي پيچيده تر(مانند کلاس ها)
*استفاده از سوکت هاي آسنکرون
*Multicasting
*Broadcasting
*نحوه تبديل ترتيب بايت ماشين به ترتيب بايت شبکه و بالعکس
*استفاده از نخ ها (Thread) براي متصل شدن چندين سرويس گيرنده بطور همزمان به سرويس دهنده

سخن پاياني

در اين مقاله به نسبت کامل، شاهد مباحث تخصصي تئوري و عملي (بطورهمزمان)بوده ايد که البته مخاطبين آن صرفاً برنامه نويسان نرم افزارهاي تحت شبکه و يا علاقمندان اين عرصه هستند. در پايان اميدواريم که با مطالعه اين مقاله، حس کنجکاوي شما برانگيخته شده و به تحقق و مطالعه بيشتر در اين زمينه بپردازيد.
منبع:نشريه دانش و کامپيوتر شماره 85




نظرات کاربران
ارسال نظر
با تشکر، نظر شما پس از بررسی و تایید در سایت قرار خواهد گرفت.
متاسفانه در برقراری ارتباط خطایی رخ داده. لطفاً دوباره تلاش کنید.
مقالات مرتبط