English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Раньше я видел статью, которая рассказывала о пяти способах коммуникации между App, они分别是 URL Scheme, Keychain, UIPastedboard, UIDocumentInteractionController и использование socket для локальной коммуникации. Первые четыре я уже использовал, и они относительно просты, всего несколько строк кода. Для последнего я никогда не использовал (извините, я все еще новичок), поэтому сегодня я попробовал это и записал здесь, чтобы поделиться с вами.
Итак, не будем терять времени, начнем:
Сначала давайте разберемся с принципом, на самом деле это очень просто, один App в локальном порту выполняет bind и listen TCP, а другой App в локальном том же порту выполняет 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(@"ошибка сокета : %d", sock);<br> return; }
2,绑定本机地址和端口号
// 地址结构体数据,记录ip和端口号 struct sockaddr_in sockAddr; // 声明使用的协议 sockAddr.sin_family = AF_INET; // 获取本机的ip,转换成char类型的 const char *ip = [[self getIPAddress] cStringUsingEncoding:NSASCIIStringEncoding]; // 将ip赋值给结构体,inet_addr()函数是将一个点分十进制的IP转换成一个长整数型数 sockAddr.sin_addr.s_addr = inet_addr(ip); // 设置端口号,htons()是将整型变量从主机字节顺序转变成网络字节顺序 sockAddr.sin_port = htons(12345); /* * bind函数用于将套接字关联一个地址,返回一个int值,-1为失败 * 第一个参数指定套接字,就是前面socket函数调用返回额套接字 * 第二个参数为指定的地址 * 第三个参数为地址数据的大小 */ int bd = bind(sock,(struct sockaddr *) &sockAddr, sizeof(sockAddr)); if(bd == -1){ close(sock); NSLog(@"bind error : %d",bd); return; }
3,监听绑定的地址
/* * listen函数使用主动连接套接接口变为被连接接口,使得可以接受其他进程的请求,返回一个int值,-1为失败 * 第一个参数是之前socket函数返回的套接字 * 第二个参数可以理解为连接的最大限制 */ int ls = listen(sock,20); if(ls == -1){ close(sock); NSLog(@"listen error : %d",ls); return; }
4,下面就是等待客户端的连接,使用accept()(由于accept函数会阻塞线程,在等待连接的过程中会一直卡着,所以建议将其放在子线程里面)
// 1,开启一个子线程 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, затем цикл while будет продолжаться, поэтому проверка на 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, как и сервер
int sock = socket(AF_INET, SOCK_STREAM,0); if(sock == -1){ NSLog(@"ошибка сокета : %d",sock); return; }
2, получение адреса хоста
NSString *host = [self getIPAddress]; // Получение IP-адреса этой машины // Возвращает указатель на структуру hostent, содержащую информацию о хосте с заданным именем struct hostent *remoteHostEnt = gethostbyname([host UTF8String]); if(remoteHostEnt == NULL){ close(sock); NSLog(@"не удалосьresolved имя хоста сервера"); return; <br>// Настройка IP-адреса и порта хоста, к которому будет подключен сокет, для функции connect() 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() для подключения к хосту
/* * Функция connect обычно используется для создания TCP-соединения клиентом с указанным адресом хоста, функция возвращает значение int, -1 - это сигнал об ошибке * Первый параметр - сокет, созданный функцией socket, который будет подключен к указанному хосту * Второй параметр - адрес и порт хоста, к которому хочет подключиться сокет sock * Третий параметр - размер адреса хоста */ 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{ // Получение данных,放置 в под-thread 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, два проекта, если у вас есть интерес, вы можете их попробовать.
https://pan.baidu.com/s/1nvcvC8p
Вот так и выглядит информация о коммуникации между приложениями iOS - local socket, продолжение будет добавлено позже, спасибо всем за поддержку нашего сайта!
Заявление: содержимое этой статьи взято из Интернета, авторские права принадлежат соответствующему автору, материал был предоставлен пользователями Интернета по своей инициативе, этот сайт не имеет права собственности, материал не был отредактирован вручную, и сайт не несет ответственности за связанные с этим юридические последствия. Если вы обнаружите контент,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (при отправке письма замените # на @) для подачи жалобы,并提供相关证据. Если подтвердится, что материал涉嫌侵权, сайт немедленно удалят涉嫌侵权的内容。