English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
رأيت مرة مقالة تُشرح خمس طرق للتواصل بين التطبيقات،وهي: URL Scheme،Keychain،UIPastedboard،UIDocumentInteractionController وكذلك استخدام socket للتواصل المحلي.الخمس الأولى قد استخدمتها جميعًا وهي بسيطة نسبيًا،أمر بسيط من بضع سطور من البرمجة.بالنسبة للخامسة الأخيرة لم أستخدمها قط (أعذروني لأني مبتدئ)،لذا حاولت الكتابة اليوم وأود مشاركتها هنا.
حسنًا،دون إطالة،بدءًا:
أولاً،نذكر مبدأه،فهو بسيط للغاية،يقوم تطبيق في نفس المنفذ المحلي بـ TCP bind وlisten،وآخر تطبيق في نفس المنفذ المحلي بـ connect،وإذن تم إنشاء اتصال TCP عادي،يمكن نقل أي بيانات تُراد.نبدأ أولاً بإنشاء الخادم:
1、أولاً،استخدم دالة socket() لإنشاء واجهة اتصال
/* * يعود socket قيمة int،-1 يعني فشل في إنشاء * الأول يُحدد عائلة المعيار/النطاق،عادةً ما يكون AF_INET(IPV4)،AF_INET6(IPV6)،AF_LOCAL * الثانية تُحدد نوع واجهة الاتصال: SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET * الثالثة تُحدد المعيار المُناسب للنقل،مثل TCP/UDP،عادةً يتم تعيينها 0 لتستخدم القيمة الافتراضية */ int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1){ close(sock); NSLog(@"socket error : %d",sock);<br> return; } /* * يعود socket قيمة int،-1 يعني فشل في إنشاء * الأول يُحدد عائلة المعيار/النطاق،عادةً ما يكون AF_INET(IPV4)،AF_INET6(IPV6)،AF_LOCAL * الثانية تُحدد نوع واجهة الاتصال: SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET * الثالثة تُحدد المعيار المُناسب للنقل،مثل TCP/UDP،عادةً يتم تعيينها 0 لتستخدم القيمة الافتراضية */ int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1){ close(sock); NSLog(@"socket error : %d",sock);<br> return; }
2、Bind the local machine address and port number
// Address structure data, record ip and port number struct sockaddr_in sockAddr; // Declare the protocol used sockAddr.sin_family = AF_INET; // Get the IP of the local machine, converted to char type const char *ip = [[self getIPAddress] cStringUsingEncoding:NSASCIIStringEncoding]; // Assign ip to the structure, inet_addr() function is to convert a dotted decimal IP into a long integer type sockAddr.sin_addr.s_addr = inet_addr(ip); // Set the port number, htons() is to convert an integer variable from host byte order to network byte order sockAddr.sin_port = htons(12345); /* * The bind function is used to associate the socket with an address, returning an int value, -1 for failure * The first parameter specifies the socket, which is the socket returned by the previous socket function call * The second parameter is the specified address * The third parameter is the size of the address data */ int bd = bind(sock,(struct sockaddr *) &sockAddr, sizeof(sockAddr)); if(bd == -1){ close(sock); NSLog(@"bind error : %d",bd); return; }
3、Listen to the bound address
/* * The listen function uses an active connection socket interface to become a connected interface, so that it can accept requests from other processes, returning an int value, -1 for failure * The first parameter is the socket returned by the previous socket function * The second parameter can be understood as the maximum limit of the connection */ int ls = listen(sock,20); if(ls == -1){ close(sock); NSLog(@"listen error : %d",ls); return; }
4、Here is waiting for the client's connection, using accept()(since the accept function will block the thread, it will be stuck during the waiting process, so it is recommended to put it in a child thread)
// 1. Start a child thread NSTread *recvThread = [[NSThread alloc] initwithTarget:self selector:@selector(recvData) object: nil]; [recvThread start]; - (void)recvData{ // 2. الانتظار حتى وصول اتصال العميل // يتم إعلان بنية العنوان، لتلقي عنوان العميل العائد فيما بعد struct sockaddr_in recvAddr; // حجم العنوان socklen_t recv_size = sizeof(struct sockaddr_in); /* * يعود دالة accept() عند إتمام الاتصال بنجاح بنية جديدة (self.newSock) تستخدم لاحقًا لإرسال واستقبال البيانات مع هذا العميل * الأول هو套تصال الاستماع، الذي كان متغيرًا محليًا، والآن يجب تحويله إلى متغير عالمي * الثانية هي أيضًا معلمة نتيجة، تستخدم لاستقبال القيمة العائدة، التي تحدد عنوان العميل * الثالثة هي أيضًا معلمة نتيجة، تستخدم لاستقبال معلومات بنية recvAddr، لتعيين حجمها بالبايتات */ self.newSock = accept(self.sock,(struct sockaddr *) &recvAddr, &recv_size); // 3. عند الوصول إلى هنا، يعني أننا قد وصلنا إلى عميل جديد، يمكننا الآن إرسال واستقبال البيانات، حيث يتم استخدام دالة send() و recv() بشكل رئيسي ssize_t bytesRecv = -1; // حجم البايتات للبيانات العائدة char recvData[128] = ""; // منطق التخزين للبيانات العائدة // إذا انقطع الاتصال من أحد الجانبين، فإن recv سيعود فورًا، ويكون bytesrecv يساوي 0، ثم سيستمر الدوران في الحلقة، لذا فإن الحكم بـ 0 هو الخروج while(1){ bytesRecv = recv(self.newSocket,recvData,128,0); // recvData هو البيانات المستلمة if(bytesRecv == 0){ break; } } }
5- إرسال البيانات
- (void)sendMessage{ char sendData[32] = "hello client"; ssize_t size_t = send(self.newSocket, sendData, strlen(sendData), 0); }
في جانب العميل، يتم تقسيم العمل بشكل رئيسي إلى: إنشاء套تصال، الحصول على عنوان الخادم من خلال ip ووصلة، ثم الاتصال، وبعد إتمام الاتصال الناجح يمكننا إرسال واستقبال البيانات من الخادم، دعونا نرى الكود الآن.
1. كما في الجانب الآخر من الخادم، أنشئ سوكت باستخدام socket function
int sock = socket(AF_INET, SOCK_STREAM,0); if(sock == -1){ NSLog(@"خطأ سوكت : %d",sock); return; }
2. الحصول على عنوان المضيف
NSString *host = [self getIPAddress]; // الحصول على عنوان IP الخاص بالمضيف // يعود pointer إلى هيكل hostent يحتوي على اسم المضيف و عنوانه struct hostent *remoteHostEnt = gethostbyname([host UTF8String]); if(remoteHostEnt == NULL){ close(sock); NSLog(@"فشل تحليل اسم المضيف للخادم"); return; <br>// إعداد عنوان IP وポート المضيف الذي سيتم ربطه به، لاستخدام connect() function struct in_addr *remoteInAddr = (struct in_addr *)remoteHost->h_addr_list[0]; struct sockaddr_in socktPram; socketPram.sin_family = AF_INT; socketPram.sin_addr = *remoteInAddr; socketPram.sin_port = htons([port intValue]);
3. استخدم connect() function للربط بالمضيف
/* * تستخدم connect function عادةً في العميل للانضمام إلى اتصال TCP، ربط المضيف المحدد، يعود القيمة int،-1 يعني فشل * الأول هو سوكت تم إنشاؤه بواسطة socket function، يمثل هذا السوكت رغبة في ربطه بمضيف معين * الثاني هو عنوان المضيف وポート المطلوب ربطه بسوكت sok * حجم الثالث هو حجم عنوان المضيف */ int con = connect(sock, (struct sockaddr *) &socketPram, sizeof(socketPram)); if(con == -1){ close(sock); NSLog(@"فشل الربط"); return; } NSLog("الربط الناجح"); // وصول إلى هنا يعني نجاح الربط;
4- بعد الاتصال الناجح، يمكنك إرسال واستقبال البيانات
- (IBAction)senddata:(id)sender { // إرسال البيانات char sendData[32] = "hello service"; ssize_t size_t = send(self.sock, sendData, strlen(sendData), 0); NSLog(@"%zd",size_t); } - (void)recvData{ // استقبال البيانات، وضعها في النواة الفرعية ssize_t bytesRecv = -1; char recvData[32] = ""; while (1) { bytesRecv = recv(self.sock, recvData, 32, 0); NSLog(@"%zd %s",bytesRecv,recvData); if (bytesRecv == 0) { break; } } }
حسنًا، هذا هو كل ما يجب القيام به باستخدام socket للتواصل بين تطبيقين محليين. أول مرة أكتب فيها مقال، لأكتب مشاعري وأشاركها معكم، إذا كان هناك شيء غير صحيح في النص، أرجو منكم أن تشيروا عليه. وفي النهاية، سأضيف عنوان Demo، هناك مشاريعان، إذا كان لديكم اهتمام، يمكنكم تنزيلها للتجربة:
هذا هو نهاية محتوى هذا المقال، نأمل أن يكون قد ساعد في تعلمكم، ونأمل أيضًا أن تدعموا تعليمات الركض.!
بيان: محتوى هذا المقال تم جمعه من الإنترنت، ملكية الملكية للحقوق التأليف والنشر للمالك الأصلي، تم جمع المحتوى من قبل المستخدمين على الإنترنت بتحميله بأنفسهم، هذا الموقع لا يملك حقوق الملكية، لم يتم تعديل المحتوى بشكل إنساني، ولا يتحمل أي مسؤولية قانونية مرتبطة بذلك. إذا كنت قد وجدت محتوى يشتبه في حقوق النسخ، فلا تتردد في إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال '#' بـ '@') لإبلاغنا، وقدم الأدلة ذات الصلة، إذا تم التحقق من ذلك، فإن هذا الموقع سيقوم بإزالة المحتوى المشبوه في حقوق النسخ على الفور.