الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

البنية التحتية لبروتوكول Http وبرمجة مستعرضات الانترنت

بدأه garo في 15 أبريل 2008 · 9 رد · 3,351 مشاهدة · في قسم المواضيع الهامة في قسم السي /سي++
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

بسم الله الرحمن الرحيم

مقدمة:

كثيرا منا لا يعرف أي شي عن برتوكول http وعن كيفية التعامل معه برمجيا باستخدام لغة السي

وقد اًليت عن نفسي الا ان أوضح ألي الأخوة كيفية الاستفادة من هذا البرتوكول وتوضيح نوع التطبيقات التي يستخدم فيها واستخداماته.

ما هو برتوكول http:

هو عبارة عن برتوكول يستخدم في نقل البيانات بين خادم الانترنت وعميل الانترنت حيث يعتبر خادم الانترنت هو احدي مواقع الويب

وعميل الانترنت هو مستعرض الويب. لقد بداء استخدام برتوكول http في الويب منذ عام 1990 . والإصدار الأول

HTTP/0.9 كان إصدار بسيط ولكن الإصدارات التي تليه HTTP/1.0- HTTP/1.1أصبحت أكثر تعقيدا.

يستخدم برتوكول http في كثير من البرامج التي تتعامل مع مواقع الويب ومن أشهر استخدامات برتوكول http هو استخدامه

في مستعرضات الانترنت.

بنية برتوكول http:

يعتمد برتوكول http علي برتوكول TCP/IP في إرسال البيانات والمنفذ الرئيسي هو 80 كما يمكن استخدام منافذ أخري ،

بإمكان برتوكول http إرسال أي نوع من البيانات بين الخادم والعميل وهذا البيان يمكن أن تكون نصوص أو صور أو أي نوع أخر.

منذ الإصدار HTTP/1.0 أصبح إرسال المعلومات عن طريق برتوكول http بطريقة الرسائل النصية.

تحتوي هذه الرسائل علي معلومات meta وتسمي المحتوي، لهذه الرسائل صيغة معينة يمكن معرفتها بالرجوع إلي rfc2068.

الطلب Request والاستجابة Response:

يطلب العميل البيانات من الخادم في صورة رسالة تسمي هذه العملية "طلب " Request ثم يفسر الخادم هذه الرسالة

ويرد علي العميل برسالة وتسمي هذه العملية "استجابة" Response.

تنقسم رسالة الطلب ألي ثلاث أجزء وهي سطر الطلب(Request line) و Request Headers و Body.

يحتوي Request Headers علي معلومات تصف نوع مستعرض العميل و رقم إصدار برتوكول http ومعلومات عامة عن الطلب، يمكن

أن تحتوي رسالة الطلب علي header واحد أو أكثر. الجسم (body) هو محتوي الطلب وهو يصف المعلومات التي يرسلها العميل

(مثل مدخلات form أو بيانات ملف لرفعه إلي الخادم) .

تحتوي رسالة الاستجابة علي سطر الحالة Status-Line و Response headers والجسم .

سطر الحال يصف حالة الرد ان كان هنالك خطا ما (مثلا الصفحة المطلوبة غير موجودة)، اما اذا لم

يكن هنالك خطاء فان الجسم يحتوي علي بيانات الصفحة ألمراده.

برمجة http:

هنالك عدة تقنيات مبنية لتسهيل عملية برمجة برتوكول http ونذكر منها WinInit وهي من أفضل التقنيات لاستخدامات ftp

والاستخدامات الغير معقدة من العمليات البسيطة من جانب العميل. تقنية WinHttp هي أكثر تعقيدا من WinInit وهي الأنسب

للبرامج التي لديها عمليات معقدة وتستخدم في جانب السيرفر والعميل معا ،وهنالك إجراءات Http Api وتستخدم من جانب السيرفر

فقط وتعتبر low-level .

يمكن استخدام السوكت socket طالما أن برتوكول http يعتمد علي tcp/ip في عمليات الإرسال وهي ما سوف نستخدمها في هذا المقال.

لقد استخدمت هذه الطريقة سابقا في برنامج محرر الفريق العربي.

الاتصال بالسيرفر:

يجب عليك أنشاء اتصال بالسيرفر قبل عملية إرسال الطلب ،وتتم بترجمة اسم المجال إلي عنوان IP المناسب وإجراء الاتصال وهذا

يتم بكل سهولة باستخدام socket.

SOCKET socket;
WSADATA WsaData;
SOCKADDR_IN sin;
socket  = socket (AF_INET, SOCK_STREAM, 0);
if(socket == INVALID_SOCKET)
		return 0;

sin.sin_family = AF_INET;
sin.sin_port = htons( (unsigned short)80 );
sin.sin_addr.s_addr = *((DWORD*)(gethostbyname( "www.arabteam2000.com" )->h_addr_list));
if(connect(socket,(LPSOCKADDR)&sin, sizeof(SOCKADDR_IN) == 0)
	return 0;

إرسال الطلب:

أولا يتم إرسال سطر الطلب ثم الرؤؤس ثم الجسم ويتم فصل الرؤؤس وسطر عن الجسم عن طريق سطر جديد خالي(“\r\n”).

إرسال سطر الطلب:

يحتوي سطر الطلب علي نوع Method و مسار الصفحة المرادة وإصدار برتوكول http، مثلا عند طلب الصفحة الرئيسية باستخدام

GET يكون سطر الطلب بالصورة التالية:

“GET /vb/index.php?t=23 HTTP/1.1\r\n”

send(socket ,“GET * HTTP/1.1\r\n”,strlen(“GET * HTTP/1.1\r\n”),0);

إرسال الرؤؤس:

تنقسم الرؤؤس إلي ثلاث أنواع ، النوع الأول يسمي general header أو الرؤؤس العامة وهي رؤؤس ترسل مع Request و

Response معا أي يمكن إرسالها مع أي منهما، النوع الثاني Request headers وهي رؤؤس خاصة بالطلب

نفسه، إما النوع الثالث فهو entity headers وهي رؤؤس خاصة بالمحتوي .

send(socket ,“accept: */*\r\n”,strlen(“accept: */*\r\n”),0);
send(socket ,“User-Agent: mozila 4.0 \r\n”, strlen(“User-Agent: mozila 4.0 \r\n”),0);

إرسال الجسم:

قبل ان يتم ارسال الجسم (المحتوي) يجب اول معرفة حجم المحتوي وتعريفه مسبقا في الرؤؤس ثم ارسال سطر خالي ثم ارسال المحتوي.

send(socket , “Content-Length: 3495 \r\n”,strlen(“Content-Length: 3495 \r\n”),0);
send(socket ,”\r\n”,strlen(“\r\n”),0);
data = m_file.Read(1/*byte*/);
do
{
send(socket ,data,1,0);
}while((data = m_file.Read(1/*byte*/) != 0))

تم تعديل هذه المشاركة بواسطة garo في 16 أبريل 2008 في 19:30

1

( _ /)

(='.'=)

(")-(")

#2

اشكرك اخي garo على الموضوع الرائع

ومتألق كما هي العادة ,

حبيت ان اضيف واتكلم عن ملف الsocket

هذا تعريف الملف الـ Header لـــsocket

#ifndef SOCKET_H
#define SOCKET_H

#include <WinSock2.h>

#include <string>

enum TypeSocket {BlockingSocket, NonBlockingSocket};

class Socket {
public:

  virtual ~Socket();
  Socket(const Socket&);
  Socket& operator=(Socket&);

  std::string ReceiveLine();
  std::string ReceiveBytes();

  void   Close();

  // The parameter of SendLine is not a const reference
  // because SendLine modifes the std::string passed.
  void   SendLine (std::string);

  // The parameter of SendBytes is a const reference
  // because SendBytes does not modify the std::string passed 
  // (in contrast to SendLine).
  void   SendBytes(const std::string&);

protected:
  friend class SocketServer;
  friend class SocketSelect;

  Socket(SOCKET s);
  Socket();


  SOCKET s_;

  int* refCounter_;

private:
  static void Start();
  static void End();
  static int  nofSockets_;
};

class SocketClient : public Socket {
public:
  SocketClient(const std::string& host, int port);
};

class SocketServer : public Socket {
public:
  SocketServer(int port, int connections, TypeSocket type=BlockingSocket);

  Socket* Accept();
};

// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/wsapiref_2tiq.asp
class SocketSelect {
  public:
	SocketSelect(Socket const * const s1, Socket const * const s2=NULL, TypeSocket type=BlockingSocket);

	bool Readable(Socket const * const s);

  private:
	fd_set fds_;
}; 



#endif

وهذا تعريف الـ socket

#include "Socket.h"
#include <iostream>

using namespace std;

int Socket::nofSockets_= 0;

void Socket::Start() {
  if (!nofSockets_) {
	WSADATA info;
	if (WSAStartup(MAKEWORD(2,0), &info)) {
	  throw "Could not start WSA";
	}
  }
  ++nofSockets_;
}

void Socket::End() {
  WSACleanup();
}

Socket::Socket() : s_(0) {
  Start();
  // UDP: use SOCK_DGRAM instead of SOCK_STREAM
  s_ = socket(AF_INET,SOCK_STREAM,0);

  if (s_ == INVALID_SOCKET) {
	throw "INVALID_SOCKET";
  }

  refCounter_ = new int(1);
}

Socket::Socket(SOCKET s) : s_(s) {
  Start();
  refCounter_ = new int(1);
};

Socket::~Socket() {
  if (! --(*refCounter_)) {
	Close();
	delete refCounter_;
  }

  --nofSockets_;
  if (!nofSockets_) End();
}

Socket::Socket(const Socket& o) {
  refCounter_=o.refCounter_;
  (*refCounter_)++;
  s_		 =o.s_;

  nofSockets_++;
}

Socket& Socket::operator=(Socket& o) {
  (*o.refCounter_)++;

  refCounter_=o.refCounter_;
  s_		 =o.s_;

  nofSockets_++;

  return *this;
}

void Socket::Close() {
  closesocket(s_);
}

std::string Socket::ReceiveBytes() {
  std::string ret;
  char buf[1024];

  while (1) {
	u_long arg = 0;
	if (ioctlsocket(s_, FIONREAD, &arg) != 0)
	  break;

	if (arg == 0)
	  break;

	if (arg > 1024) arg = 1024;

	int rv = recv (s_, buf, arg, 0);
	if (rv <= 0) break;

	std::string t;

	t.assign (buf, rv);
	ret += t;
  }

  return ret;
}

std::string Socket::ReceiveLine() {
  std::string ret;
  while (1) {
	char r;

	switch(recv(s_, &r, 1, 0)) {
	  case 0: // not connected anymore;
			  // ... but last line sent
			  // might not end in \n,
			  // so return ret anyway.
		return ret;
	  case -1:
		return "";
//	  if (errno == EAGAIN) {
//		return ret;
//	  } else {
//	  // not connected anymore
//	  return "";
//	  }
	}

	ret += r;
	if (r == '\n')  return ret;
  }
}

void Socket::SendLine(std::string s) {
  s += '\n';
  send(s_,s.c_str(),s.length(),0);
}

void Socket::SendBytes(const std::string& s) {
  send(s_,s.c_str(),s.length(),0);
}

SocketServer::SocketServer(int port, int connections, TypeSocket type) {
  sockaddr_in sa;

  memset(&sa, 0, sizeof(sa));

  sa.sin_family = PF_INET;			 
  sa.sin_port = htons(port);		  
  s_ = socket(AF_INET, SOCK_STREAM, 0);
  if (s_ == INVALID_SOCKET) {
	throw "INVALID_SOCKET";
  }

  if(type==NonBlockingSocket) {
	u_long arg = 1;
	ioctlsocket(s_, FIONBIO, &arg);
  }

  /* bind the socket to the internet address */
  if (bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in)) == SOCKET_ERROR) {
	closesocket(s_);
	throw "INVALID_SOCKET";
  }

  listen(s_, connections);							   
}

Socket* SocketServer::Accept() {
  SOCKET new_sock = accept(s_, 0, 0);
  if (new_sock == INVALID_SOCKET) {
	int rc = WSAGetLastError();
	if(rc==WSAEWOULDBLOCK) {
	  return 0; // non-blocking call, no request pending
	}
	else {
	  throw "Invalid Socket";
	}
  }

  Socket* r = new Socket(new_sock);
  return r;
}

SocketClient::SocketClient(const std::string& host, int port) : Socket() {
  std::string error;

  hostent *he;
  if ((he = gethostbyname(host.c_str())) == 0) {
	error = strerror(errno);
	throw error;
  }

  sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr = *((in_addr *)he->h_addr);
  memset(&(addr.sin_zero), 0, 8); 

  if (::connect(s_, (sockaddr *) &addr, sizeof(sockaddr))) {
	error = strerror(WSAGetLastError());
	throw error;
  }
}

SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type) {
  FD_ZERO(&fds_);
  FD_SET(const_cast<Socket*>(s1)->s_,&fds_);
  if(s2) {
	FD_SET(const_cast<Socket*>(s2)->s_,&fds_);
  }	 

  TIMEVAL tval;
  tval.tv_sec  = 0;
  tval.tv_usec = 1;

  TIMEVAL *ptval;
  if(type==NonBlockingSocket) {
	ptval = &tval;
  }
  else { 
	ptval = 0;
  }

  if (select (0, &fds_, (fd_set*) 0, (fd_set*) 0, ptval) == SOCKET_ERROR) 
	throw "Error in select";
}

bool SocketSelect::Readable(Socket const* const s) {
  if (FD_ISSET(s->s_,&fds_)) return true;
  return false;
}

يوجد ايضا لدي امثله للمهتمين في هذا المجال ,

اشكرك مرة اخرى

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#3

شكرا اخي time1.هذه الطبقة قد تساعد كثيرا.

لكني فضلت استخدام الدوال حتي لا اشغل بال القراء عن الموضوع الاساسي (برمجة http)

شكرا اخي مرة اخري.

( _ /)

(='.'=)

(")-(")

#4

السلام عليكم

اخواني جزاكم الله خير الجزاء على مجهودكم

ولي منكم طلب

اريد ان تضعو كود لــ

client /server chat

باستخدام الـ

C++

واريد ان تشرحو لي طريقة تطبيقه

وشكرا لكم

تم تعديل هذه المشاركة بواسطة girlwise في 17 أبريل 2008 في 12:37

#5

تفضلـ(ي) هذا الموقع قد يفيدك

client-server

llback.jpg

اشهد ان لا إله إلا الله وان محمدا ً رسول الله

#6

الاخ time1 :

جزاك الله خير

في الحقيقه انا لدي الكود

الكود الاول لل serverوالاخر لل client

ولكن لا اعرف طريقة التطبيق

انا اريد ان يقوم شخصين بتطبيقه

باستخدام جهازين احدهم كعميل والاخر كخادم

ولكن كيف اجعل هذين الكودين يعملان كبرنامج محادثه

عفوا انا لا اريد ان اخرج عن الموضوع الاساسي ولكن اريد ان اتعلم ذالك

وشعرت ان هذا الموضوع قريب لاضع استفساري هنا

شكرا لك مره اخرى

#7

بالنسبة للشات يمكن انشاء ذلك باستخدام السوكت

عن طريق انشاء رسائل خاصة (بروتوكول)بين البرنامجين (العميل والخادم)

بحيث تتضمن هذه الرسائل البيانات التي نريد ارسالها بين البرنامجين مثل النصوص او الصور او اي شي

( _ /)

(='.'=)

(")-(")

#8

عفوا اخي

هل ممكن تزويدي بمكتبات لغة الـC++ التي تستخدم في برمجة الشبكات وخصوصا Client/server

وشكرا لك

#10

السلام عليكــم ورحمـة الله وبركاتــه ،،

up

مواضيع مشابهة