0

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

 
amirpetrucci0261
amirpetrucci0261
کاربر طلایی1
تاریخ عضویت : تیر 1388 
تعداد پست ها : 27726
محل سکونت : http://zoomstar.ir/

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

کدهایی که تا به اینجا دیدیم برای ایجاد سوکت های همگام یا سنکرون بوده است. این سوکت ها در برنامه های ویندوز و کلا سیستم های مالتی تسک کاربردی ندارند. چرا که بالفرض در زمانی از متد 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,Mainlistener);

در این مثال ، مشخص کرده ایم که سوکت شروع به عمل گوش دادن و انتظار کند و سپس به محض کانکت شدن یک کلاینت به کامپیوتر ما ، تابع AcceptCallBack اجرا گردد و به اموری که تعیین میکنیم رسیدگی کند.


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


تابع 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);

حال میتوانیم اطلاعات را نمایش دهیم . نکته مهمی که اینجا وجود دارد این است که در برنامه نویسی به شیوه آسنکرون دیگر نمیتوانیم به صورت عادی این رشته را به یک TextBox و یا کلا یک کنترل بفرستیم . دلیل این امر این است که تنها آن thread ای که کنترل مورد نظر را در ابتدای برنامه ایجاد کرده است میتواند با کنترل کار کرده و اطلاعات لازم را به آن بفرستد . پس کاری که انجام میدهیم استفاده از یک متد کمکی است که ابتدا thread ایجاد کننده کنترل را پیدا کرده و سپس کار مورد نظر را با استفاده از آن انجام میدهد . در این مثال نام این تابع را ShowInfo قرار داده ایم و تنها یک پارامتر از نوع رشته ای به آن ارسال میکنیم . البته فعلا دانستن جزئیات این متد چندان لازم نیست :

delegate void ShowInfoCallback(string text);
private void ShowInfo(string text)
{
if (this.txtMain.InvokeRequired)
{
ShowInfoCallback d = new ShowInfoCallback(ShowInfo);
this.Invoke(d, new object[] { text });
{
else
{
txtMain.Text+=text+"\n";
}
}

نکته : در برنامه اصلی فرض کرده ایم ، کنترلی که قرار است اطلاعات را نمایش دهد یک RichTextBox به نام txtMain میباشد.


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

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

پس متد ReceiveCallBack به این صورت است :

private void ReceiveCallBack(IAsyncResult ar)
{
Socket worker = ((Socket)ar.AsyncState);
int bytesReceived = worker.EndReceive(ar);
string str = System.Text.UTF8Encoding.UTF8.GetString(buffer);
ShowInfo(str);
AsyncCallback ReceiveMethod = new AsyncCallback(ReceiveCallBack);
worker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None,
ReceiveMethod, worker);
}

متن کامل برنامه :

byte[] buffer = new byte[1024];
public Form1()
{
InitializeComponent();
Socket Mainlistener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
IPEndPoint server = new IPEndPoint(IPAddress.Any, 1800);
Mainlistener.Bind(server);
AsyncCallback callBackMethod = new AsyncCallback(AcceptCallback);
Mainlistener.Listen(4);
Mainlistener.BeginAccept(AcceptCallback,Mainlistener);
}
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, ReceiveMethod, worker);
}
private void ReceiveCallBack(IAsyncResult ar)
{
Socket worker = ((Socket)ar.AsyncState);
int bytesReceived = worker.EndReceive(ar);
string str = System.Text.UTF8Encoding.UTF8.GetString(buffer);
ShowInfo(str);
AsyncCallback ReceiveMethod = new AsyncCallback(ReceiveCallBack);
worker.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveMethod, worker);
}
delegate void ShowInfoCallback(string text);
private void ShowInfo(string text)
{
if (this.txtMain.InvokeRequired)
{
ShowInfoCallback d = new ShowInfoCallback(ShowInfo);
this.Invoke(d, new object[] { text });
}
else
{
txtMain.Text+=text+"\n";
}
}

برای تست کردن برنامه میتوانیم از Telnet استفاده کنیم. Telnet یکی از امکانات ویندوز است که به کمک ان میتوان به یک آدرس IP و یک شماره پورت خاص وصل شده و به ارسال اطلاعات مشغول شویم. کافی است ایتدا برنامه را اجرا کرده و سپس در منوی RUN تایپ کنید :

telnet 127.0.0.1 1800

سپس در پنجره مشکی رنگ ظاهر شده ، شروع به تاپیک کردن کنید . آنچه شما تایپ میکنید، برای برنامه ارسال میگردد و میتوانید آن را مشاهد فرمایید .


نکته مهم این است که چنانچه برنامه TELNET را پیش از بستن برنامه سرور که نوشته ایم ببندید ، برنامه ما در یک loop می افتد و آخرین اطلاعات ارسال شده را به صورت مداوم تکرار میکند !!! البته این مشکل از برنامه ما نیست و به دلیل عملکرد برنامه Telnet این اتفاق می افتد. در مقاله بعدی یاد خواهیم گرفت که برنامه سمت کلاینت را نیز به صورت آسنکرون بنویسیم و بنابراین نیازی به Telnet نخواهیم داشت .

چهارشنبه 10 آذر 1389  11:13 PM
تشکرات از این پست
دسترسی سریع به انجمن ها