PDA

توجه ! این یک نسخه آرشیو شده میباشد و در این حالت شما عکسی را مشاهده نمیکنید برای مشاهده کامل متن و عکسها بر روی لینک مقابل کلیک کنید : آموزشی سوکت پروگرامینگ در سی شارپ



آبجی
12th April 2010, 01:31 AM
در مدل کلاینت - سرور ، مبادلات زیر بین کلاینت و سرور رخ میدهد

1- سرور سوکتی را تعریف میکند
2 - سرور سوکت را به یک IP که همان IP خودش است و یک پورت Bind میکند یا اختصاص میدهد
3-سرور به پورت گوش میدهد
4- کلاینت سوکتی را تعریف میکند و IP و پورت سرور را به آن اختصاص میدهد
5- کلاینت درخواست اتصال یا کانکت شدن به سرور را میدهد
6-سرور درخواست کلاینت را دریافت و آن را می پذیرد

7- کلاینت اطلاعاتی را ارسال می کند
8- سرور اطلاعات را می گیرد
9- سرور اطلاعات را ارسال میکند و کلاینت آن را میگیرد
10- سرور بسته میشود
11- کلانیت بسته میشود
===============================
ابتدا برنامه سمت سرور را مینویسم .
در این برنامه می بایست یک پورت را باز کرده و به آن گوش دهیم و دریافتی را نمایش دهیم .

ابتدا می بایست فضای نامهای زیر را با استقاده از using به کامپایلر سی شارپ معرفی کنیم :


کد:using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
اکنون متغیری به نام را به صورت سراسری و static تعریف میکنیم (از آنجایی که متد Main یک متد static است کلیه متغیرها و توابع مورد استفاده در آن نیز باید static باشند.)

متغیرها و متدهای Static را میتوان اینگونه تعریف کرد :
متغیرها و توابعی هستند که در یک کلاس به طور مشترک بین کلیه اشیاء گرفته شده از آن کلاس وجود دارند نه اینکه به ازای هر شیء یک نمونه از آن ایجاد شود. دسترسی به این متغیرها از طریق نام کلاس ممکن خواهد بود.
در مثال زیر کلاسی به نام Circle تعریف کرده ایم که دارای یک متغیر عادی به نام r است . این متغیر شعاع دایره را نگه میدارد.
متغیر PI به صورت static تعریف شده است چرا که عدد پی به ازای کلیه اشیاء این کلاس ثابت است.
در کلاس MainClass از کلاس Circle یک شیء ساخته ایم و از آن استفاده کرده ایم

کد:

class Circle
{
public static float PI =3.14;
public int r;

public static void PrintIt(double dbl)
{
Console.WriteLine(dbl);
}
}

class MainClass
{
static void Main(string[] args)
{
Circle cl = new Circle();
Console.WriteLine("Please Enter The Circle R");
cl.r = Convert.ToInt32( Console.ReadLine());

Circle.PrintIt(Circle.PI * cl.r * cl.r);
}
}

حال به مطلب اصلی خود بازگردیم :
یک شیء از کلاس سوکت به صورت سراسری و static ایجاد می کنیم :
کد:
static Socket sktListener;

در متد Main این سوکت را new مینکیم تا به آن حافظه اختصاص داده شود :
کد:
tatic void Main()
{
sktListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

AddressFamily.InterNetwork به معنای این است که از شبکه ای استفاده میکنیم که دارای IP نسخه 4 است.
SocketType.Stream برای سوکت هایی است که میخواهند به صورت Stream داده ها را تبادل کنند

ProtocolType.Tcp که نوع پروتکل ما را مشخص میکند.

اکنون می باید ِم آدرس IP و یک Port به سوکت مان اختصاص دهیم :
کد:
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, 1800);
sktListener.Bind(ipLocal);

از آنجایی که این برنامه در سمت سرور اجرا میشود آدرس IP خاصی به آن نمیدهیم و پورت 1800 را باز میکنیم. کلاس IPEndPoint برای مشخص نمودن یک نود یا یک کامپیوتر در شبکه به کار میرود.

متد Bind نود مشخص شده را به سوکت اختصاص میدهد.

اکنون زمان گوش دادن به پورت است :
کد:
sktListener.Listen(100);

عدد 100 نشانه آن است که حداکثر 4 connection میتوانند در صف قرار گیرند .
اگر در این لحضه در command prompt دستور netstat –an را تایپ کنید میتوانید ببینید که پورت 1800 باز شده و در حال گوش دادن است.
حال میباید تقاضای کانکت شدن کلاینت را بپذیریم :
کد:
sktListener = sktListener.Accept();

حال برای گرفتن داده ها ، می بایست یک بافرتعریف نماییم .
نکته : در سوکت پروگرمینگ ، داده ها به صورت آرایه ای از بایت ها منتقل می شوند. برای ارسال رشته های یونیکد و .... بایست آنها را کد گذاری کنیم . برای کد گذاری و کد گشایی از کلاس System.Text و متدهای آن استفاده کنیم. مثلا دستور زیر رشته salam را با فرمت Ascii به آرایه ای از بایت ها تبدیل میکند
کد:
byte[] byt = Encoding.ASCII.GetBytes("salam");

و متد زیر آن را رمزگشایی میکند :
کد:
string str = Encoding.ASCII.GetString(byt);

ما عمل رمزنگاری را موقع ارسال داده ها و عمل رمز گشایی را موقع دریافت آنها انجام میدهیم .
اکنون میخواهیم داده ها را دریافت کرده و رمز گشایی کنیم :
کد:
byte[] buffer = new byte[500];
sktListener.Receive(buffer);
string Data = Encoding.ASCII.GetString(buffer);

حال میتوانیم داده ها را پردازش کنیم :
کد:
Console.WriteLine(Data);

====================================
سوکت های آسنکرون

کدهایی که تا به اینجا دیدیم برای ایجاد سوکت های همگام یا سنکرون بوده است. این سوکت ها در برنامه های ویندوز و کلا سیستم های مالتی تسک کاربردی ندارند. چرا که بالفرض در زمانی از متد accept استفاده نموده ایم، در این حالت برنامه تا رسیدن یک سوکت به آن قفل شده و قادر به انجام کاری نیست.

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

نکته : قبل از ادامه ، آشنایی با delegate ها الزامی است. میتوانید delegate ها را در مقاله مربوطه مشاهده فرمایید ولی اگر بخواهیم در یک جمله Delegate ها را تعریف کنیم میتوانیم بگوییم :
delegate در حقیقت چیزی نیست جز اشاره گر به تایع !

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

نکته : متدهای آسنکرون با پیشوندهای Begin و End شروع میگردند.

برای نوشتن برنامه ، ابتدا یک سوکت تعریف میکنیم که عمل گوش دادن را انجام دهد :

کد:
Socket Mainlistener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

سپس عملیات معمول را بر روی سوکت انجام میدهیم :

کد:
IPEndPoint server = new IPEndPoint(IPAddress.Any, 1800);
Mainlistener.Bind(server);

همانگونه که میبینید ، در این برنامه سوکت مورد نظر ما به پورت 1800 گوش میدهد.

اکنون زمان آن است که یک delegate ایجاد کرده و آن را به تایع پردازشگر که در این مثال AcceptCallBack نام دارد ، منتسب کنیم .


کد:
AsyncCallback callBackMethod = new AsyncCallback(AcceptCallback);

اکنون باید سوکت تعریف شده به صورت غیر همگام ( آسنکرون) شروع به گوش دادن به پورت کند :

کد:
Mainlistener.Listen(4);
Mainlistener.BeginAccept(AcceptCallback,Mainlisten er);
در این مثال ، مشخص کرده ایم که سوکت شروع به عمل گوش دادن و انتظار کند و سپس به محض کانکت شدن یک کلاینت به کامپیوتر ما ، تابع AcceptCallBack اجرا گردد و به اموری که تعیین میکنیم رسیدگی کند.

نکته : پارامتر دوم تابع BeginAccept ، شیء ای است برای ارسال داده های وضعیت سوکت به تابعی که به سوکت رسیدگی میکند . در این جا این شیء خود سوکت است . اگر سوکت را به صورت سراسری تعریف میکردیم ، نیاز به ارسال این شیء نبود و به جای آن null قرار می دادیم. شیء مربوطه در قالب یک شیء از کلاس IAsyncResult ارسال خواهد شد.

تابع AcceptCallback بایستی اینگونه تعریف شود.

کد:
private void AcceptCallback(IAsyncResult ar)
{
...
{

تابع AcceptCallback بایستی اینگونه تعریف شود.

کد:
private void AcceptCallback(IAsyncResult ar)
{
...
}

در این تابع ، آرگومانی از نوع IAsyncResult وجود دارد. این آرگومان اطلاعات وضعیت فراخوان تابع آسنکرون که در اینجا یک سوکت است را نگهداری میکند. ابتدا این اطلاعات را استخراج میکنیم :



کد:

Socket temp = ((Socket)ar.AsyncState);


سپس به گوش دادن برای پذیرفتن کلاینت خاتمه میدهیم چرا که اکنون دیگر کلاینت مورد نظر به سرور کانکت شده و آماده برای ارسال اطلاعات است :


کد:

Socket worker = temp.EndAccept(ar);

نکته : دو دستور قبل را میتوانستیم در قالب یک دستور و به این شکل بنویسیم :


کد:

Socket temp = ((Socket)ar.AsyncState).EndAccept(ar);


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

نکته ای که بسیار حائز اهمیت است این که بایستی از یک بافر برای ذخیره اطلاعات دریافتی استافده کنیم. این بافر که در حقیقت آرایه ای از بایتها است به صورت یک آرایه سراسری تعریف میکنیم :


کد:

byte[] buffer = new byte[1024];

نکته : با توجه به این که در این برنامه صرفا یا یک کلاینت کار میکنیم ، سراسری بودن بافر مشکلی ایجاد نمیکند، اما چنانچه قصد داشتیم با چند کلاینت کار کنیم برای هریک میبایست بافر مخصوص به خودش را تعریف میکردیم که اصولا پیاده سازی آن برنامه به گونه ای دیگر خواهد بود که در مقالات آتی مورد بررسی قرار خواهد گرفت.

برای دریافت اطلاعات به صورت آسنکرون ، از متد BeginReceive استفاده میکنیم. البته بایستی بافر، اندیس اولیه ای که میخواهیم بافر از آنجا پر شود و همچنین اندیس حد نهایی بافر را مشخص کنیم.

کد:

AsyncCallback ReceiveMethod = new AsyncCallback(ReceiveCallBack);
worker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None,new
ReceiveMethod, worker);

پس تا اینجا ، متد AcceptCallback به این صورت است :


کد:
private void AcceptCallback(IAsyncResult ar)
{
Socket temp = ((Socket)ar.AsyncState);
Socket worker = temp.EndAccept(ar);
AsyncCallback ReceiveMethod = new AsyncCallback(ReceiveCallBack);
worker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None,new
ReceiveMethod, worker);
}

اکنون متد ReceiveCallBack را تعریف میکنیم :


کد:
private void ReceiveCallBack(IAsyncResult ar)
{
...
{

درون این متد ، ابتدا اطلاعات وصعیت را بدست می آوریم :


کد:

Socket worker = ((Socket)ar.AsyncState);


سپس به گوش دادن به صورت موقت خاتمه میدهیم تا بتوانیم داده های فعلی را پردازش کنیم. این کار را با متد EndReceive انجام میدهیم . مقدار بازگشتی این متد تعداد بایتهای دریافت شده میباشد :




int bytesReceived = worker.EndReceive(ar);


حال میبایست اطلاعات دریافت شده که به صورت آرایه ای بایتها درون بافر هستند را پردازش کرده و جهت نمایش به رشته (string) تبدیل کنیم :

کد:

string str = System.Text.UTF8Encoding.UTF8.GetString(buffer);


منبع Persiantools

zanbooor_2006
2nd May 2010, 10:08 AM
سلام می تونید برنامه سمت کلاینت رو هم به صورت آسنگرون بنویسید
ممنون می شم

jansakht
14th June 2010, 12:50 AM
سلام
کارتون عالی بود [khoobboodan]اگه لطف کنید برنامه سمت کلاینت رو هم بذارید دیگه حرف نداره[tashvigh]
باز هم ممنونم

hadidakhili
19th October 2014, 08:17 PM
سلام
کارتون عالی بود [khoobboodan]اگه لطف کنید برنامه سمت کلاینت رو هم بذارید دیگه حرف نداره[tashvigh]
باز هم ممنونم

با تشکر

استفاده از تمامی مطالب سایت تنها با ذکر منبع آن به نام سایت علمی نخبگان جوان و ذکر آدرس سایت مجاز است

استفاده از نام و برند نخبگان جوان به هر نحو توسط سایر سایت ها ممنوع بوده و پیگرد قانونی دارد