رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران
  • 0
kambiz behnia

Thread & Socket


سوال

باسلام

تقریبا مدت زیادی است که درگیر Socket programming  و Thread هستم. و هر چند وقت یکبار سوالاتی را در مورد چگونگی استفاده از آنها در فروم های مختلف مطرح و تا حدودی پاسخ خود را یافته ام. با توجه به اینکه گذشت زمان سوالات و شرایط تغییر کرده و لذا خواستم سوالاتی از این دست را یکبار دیگر از ابتدا مطرح و جواب کاملی برای هر کدام داشته باشم. اگر چه امکان دارد جواب هرکدام از این سوالات بطور پراکنده و جداگانه در سایتهای مختلف موجود باشد. علاوه بر آن کمتر جایی یدم که این دو موضوع را با هم بررسی کرده باشند یا فقط به مبحث Thread پرداخته اند ویا فقط درباره Socket توضیح داده اند آن هم فقط در مورد بعضی از توابع مرتبط.

حتما دوستان با ترتیب اجرای دستورات و نحوه ایجاد و مفهوم سوکتهای سمت سرور و کلاینت آشنا هستند و هفت نحوه استفاده از آنها نیست بلکه هدف این است 

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

آیا پاسخ های داده شده برای محیط Windows, Linux, ... هردو درست است

علاوه بر کتابخانه های همراه کمپایلرها کتابخانه های دیگری نظیر Boost - Poco - ACE - Qt - ... هم کتابخانه ای برای کار با شبکه دارند وضعیت توابع مشابه در آنها چگونه است.

سوالات رو از سمت سرور شروع میکنم.

فرض لازم است سمت سرور 3 سوکت سروری داشته باشیم.

SOCKET AcceptSock1;
SOCKET AcceptSock2;
SOCKET AcceptSock3;

حال باید دستورات زیر اجرا شود

int  InitFunction(SOCKET &AcceptSock)
{
	SOCKET AcceptSock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

	if (AcceptSock == INVALID_SOCKET)
	{
		printf("Function socket failed with error : %u\n", WSAGetLastError());
		return 0;
	}

	int iTimeout = 500;
	BOOL option = TRUE;

	int iResult = setsockopt(AcceptSock, SOL_SOCKET, SO_RCVTIMEO, (char *)&iTimeout, sizeof(iTimeout));

	if (iResult == SOCKET_ERROR)
	{
		printf("Function setsockopt failed with error: %u\n", WSAGetLastError());

		return 0;
	}

	SOCKADDR_IN client_sin;
	SOCKADDR_IN local_sin;


	int iAddrSize = sizeof(client_sin);
	int iPort = 12345;

	// select the local interface, and bind to it
	local_sin.sin_addr.s_addr = htonl(INADDR_ANY);
	local_sin.sin_family = AF_INET;
	local_sin.sin_port = htons(iPort);

	if (bind(AcceptSock, (struct sockaddr *)&local_sin, sizeof(local_sin)) == SOCKET_ERROR)
	{
		printf("Function bind failed with error: %u\n", WSAGetLastError());

		return 0;
	}

	if (listen(AcceptSock, 32) == SOCKET_ERROR)
	{
		printf("Function listen failed with error: %u\n", WSAGetLastError());

		return 0;
	}
}

 فانکشن بالا باید برای AcceptSock1 و AcceptSock2 و AcceptSock3 اجرا شود. با اجرای سه کد زیر.

InitFunction(AcceptSock1);
InitFunction(AcceptSock2);
InitFunction(AcceptSock3);

سوال آیا من میتوانم هرکدام از سه دستور بالا را در Thread های مختلف اجرا کنم ویا باید حتما در Thread main اجرا شود.

برای پذیرش کلاینتهایی که درخواست اتصال دارند از تابع زیر استفاده میکنیم

int  ProcessFunction(SOCKET &AcceptSock)
{
	fd_set fd;
	timeval tv;

	SOCKADDR_IN client_sin;

	int iAddrSize = sizeof(client_sin);

	while (true)
	{
		tv.tv_sec = 3;
		tv.tv_usec = 0;

		FD_ZERO(&fd);
		FD_SET(AcceptSock, &fd);


		int iResult = select(0, &fd, NULL, NULL, &tv);
		if (iResult == SOCKET_ERROR)
		{
			printf("Function select failed with error: %u\n", WSAGetLastError());

			closesocket(AcceptSock);

			return 0;
		}

		if (iResult == 0)
		{
			continue;
		}

		SOCKET soc = accept(AcceptSock, (struct sockaddr *) &client_sin, &iAddrSize);


		if (soc == INVALID_SOCKET)
		{
			printf("Function accept failed with error: %u\n", WSAGetLastError());

			continue;
		}
		
		//... Do 

	}


}

  که باید برای AcceptSock1 و AcceptSock2 و AcceptSock3 اجرا شود. با اجرای سه کد زیر.

ProcessFunction(AcceptSock1);
ProcessFunction(AcceptSock2);
ProcessFunction(AcceptSock3);

سوال

سوال آیا من میتوانم هرکدام از سه دستور بالا را در Thread های مختلف اجرا کنم

 کلا لازم نیست هیچ گونه عملیات لاکی صورت گیرد.

آیا من میتواند از AcceptSock1 در دو Thread مختلف استفاده کنم (پاسخ خیر است فقط میخواستم جواب در اینجا باشد). اگر استفاده کنم چه اتفاقی پیش میآید.

 

فانکشن ()WSAGetLastError قطعا Threadsafe نیست ولی آیا توابع دیگری که کد خطا را بروز میکنند Threadsafe نیستند.

 

برای خواندن و نوشتن اطلاعات از Socket پذیرش شده از توابع زیر استفاده می کنیم

int SendMessageFunction   (SOCKET sock, const char * msg, int len)
{
	fd_set fd;
	timeval tv;

	tv.tv_sec = 0;
	tv.tv_usec = 0;

	FD_ZERO(&fd);
	FD_SET(sock, &fd);

	int iResult = select(0, NULL, &fd, NULL, &tv);
	
	if (iResult == SOCKET_ERROR)
	{
		printf("Function SendMessage failed with error: %u\n", WSAGetLastError());
		
		return -1;
	}
	
	int	rc = send(sock, (char *)msg, len, 0);

	if (rc != len)
	{
		return -1;
	}

	return 1;
}
int ReceiveMessageFunction(SOCKET sock,       char * msg, int &len)
{
	fd_set fd;
	timeval tv;

	tv.tv_sec = 0;
	tv.tv_usec = 0;

	FD_ZERO(&fd);
	FD_SET(sock, &fd);

	int i = select(0, &fd, NULL, NULL, &tv);
	
	if (i == SOCKET_ERROR)
	{
		printf("Function ReceiveMessage failed with error: %u\n", WSAGetLastError());

		
		return -1;
		
	}
	else if (i == 0) { // no data on socket
		return 0;
	}
	//
	//...
	//len = ...	
	//...
	int rc = recv(sock, (char *)msg, len, 0);
	// ...
	return 1;
}

 

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

آیا امکان خواندن از  یک سوکت توسط دو Thread مختلف امکان پذیر است. نوشتن چطور

 آیا میتوان یک Thread از Socket بخواند و Thread دیگری در آن بنویسد.

 

با توجه به این موضوع که در یکی از صفحات اینترنتی بیان شده بود که Boost برخلاف کتابخانه های دیگر Threadsafe است (اگرچه امروز هرچه گشتم دوباره آنرا پیدا نکردم)  پاسخ با توجه به پلتفورم )Windows, Linux, ..)  و کتابخانه مورد استفاده (پیشفرض کمپایلر - ACE - Poco - Boost  Qt - ...)  در نظر گرفته شود.

متشکرم

 

 

  • تشکر شده 1

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

2 پاسخ به این سوال تا کنون داده شده است

پست های پیشنهاد شده

  • 0
در در 24 فروردین 1398 در 13:27، kambiz behnia گفته است :

سوال آیا من میتوانم هرکدام از سه دستور بالا را در Thread های مختلف اجرا کنم ویا باید حتما در Thread main اجرا شود.

 

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

 

در در 24 فروردین 1398 در 13:27، kambiz behnia گفته است :

سوال آیا من میتوانم هرکدام از سه دستور بالا را در Thread های مختلف اجرا کنم

 

بله می توانید درنخ های مختلف اجرا کنید! منتهی حتما باید از یک قفل جهت کنترل accept استفاده کنید تا race condition رخ ندهد.

در در 24 فروردین 1398 در 13:27، kambiz behnia گفته است :

آیا من میتواند از AcceptSock1 در دو Thread مختلف استفاده کنم (پاسخ خیر است فقط میخواستم جواب در اینجا باشد). اگر استفاده کنم چه اتفاقی پیش میآید.

بله می توانید از AcceptSock1 در دونخ مجزا استفاده کنید به شرط اینکه تمامی شرایط داده ناحیه اشتراکی را در نخ ها لحاظ کنید. در صورتی که ناحیه اشتراکی به درستی تعریف نشده باشد قطعا Race Condition و بعد هم Dead Lock رخ خواهد داد.

 

در در 24 فروردین 1398 در 13:27، kambiz behnia گفته است :

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

 آیا امکان خواندن از  یک سوکت توسط دو Thread مختلف امکان پذیر است. نوشتن چطور

 آیا میتوان یک Thread از Socket بخواند و Thread دیگری در آن بنویسد.

بله میتوانید اگر شرایط ذکر کرده در بالا را رعایت کنید.

بله امکان خواندن از یک سوکت هم در دونخ مجزا هم وجود دارد ولی قطعا پیچیدگی های زیادی برای داده های اشتراکی خواهید داشت.

بله ولی قطعا بدون استفاده از قفل گذاری مناسب و ناحیه اشتراکی خیر!

در پایان باید عرض کنم که استفاده از نخ ها در سیستم عامل ویندوز با استفاده از کلاس Thread می باشد.

که البته کتابخانه های زیادی برای Concurrency , Parallel Prog وجود دارد ولی تقریبا نحوه استفاده از قفلها در ویندوز و لینوکس مشابه هستند.

و در لینوکس با استفاده از کلاس pthread می توانید از نخ ها استفاده کنید. البته توجه داشته باشید که ساختمان نخ ها در هر دو سیستم عامل خیلی باهم متفاوت هستند هرچند برنامه نویس خیلی درگیر این پیچیدگی ها نیست چون خود سیستم عامل مدیریت میکنه.

بنابراین برای استفاده از سوکت ها در نخ ها ویا حوضچه های نخ حتما باید قوانین نواحی اشتراکی را مد نظر قرار دهید تا هم از بازدهی هرچه بهتر پردازشگر استفاده کنید وهم برنامه ای به مراتب سریعتر از برنامه های ترتیبی ایجاد کرده باشید.

ولی فکر کنم ذکر یک نکته خالی از لطف نباشد: تنها راه ایمن ماندن از خطاها و گرفتاری های نخ ها (استفاده نکردن از نخ هاست.) 

یعنی اگر در استفاده از نخ ها دچار افراط شوید قطعا برنامه هایی به مراتب کندتر از نسخه های ترتیبی خواهید ساخت.

  • پسندیدن 1
  • تشکر شده 1

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0

با تشکر از پاسخ

بطور خلاصه نتیجه گیری خودم از پاسخ های شما را اینجا می آورم لطفا اطلاح بفرمائید.

1- توابع socket - setsocketopt - bind و listen  برای استفاده در Threadها مشکلی ندارند و برای استفاده از آنها نیاز به استفاده متدهایی برای Lockکردن نیست (منظور استفاده از خود توابع بوده و فرض بر این است که پارامترها حتما مختلف خواهند بود)

2- برای استفاده از دستور accept باید از متدهای lock کردن منابع استفاده کرد حتی اگر پارامترها مختلف باشند.

3 - استفاده از توابع select - send , recv  همانند قسمت 1 هستند.

4 - اگر یک socket که بطور صحیح ایجاد و قابل استفاده است در اختیار داشته باشیم. میتوانیم در یک Thread فقط کار خواندن و در یک Thread دیگر فقط کار نوشتن روی آنرا انجام دهیم.

5- استفاده از تابع close شرایط واضح خود را دارد و حین و بعد از استفاده از آن پارامتر مشخص شده برای آن نباید در Thread دیگری مورد استفاده باشد.

6 - تابع connect عملکردی مشابه قسمت 5 دارد.

سوال 1: اگر مورد 2 صحیح باشد متوجه علت آن نمیشوم چو اگر بقیه برای استفاده در Thread مشکلی نداشته باشند این هم نباید داشته باشد ؟ 

چون بطور کلی در Thread ها مشکل استفاده از منابه مشترک است آیا توابع ذکرشده منبع حساب میشوند.

 

سوال 2: در بخش اول نپرسیدم نحوه استفاده از errno برای برسی خطا در سوکتها به چه شکل است هر Thread مقدار جداگانه ای برای خود دارد (بعید بنظر میرسد).

 

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

برای ارسال دیدگاه یک حساب کاربری ایجاد کنید یا وارد حساب خود شوید

برای اینکه بتوانید دیدگاهی ارسال کنید نیاز دارید که کاربر سایت شوید

ایجاد یک حساب کاربری

برای حساب کاربری جدید در سایت ما ثبت نام کنید. عضویت خیلی ساده است !

ثبت نام یک حساب کاربری جدید

ورود به حساب کاربری

دارای حساب کاربری هستید؟ از اینجا وارد شوید

ورود به حساب کاربری

  • کاربران آنلاین در این صفحه   0 کاربر

    هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.

×