English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Пример коммуникации iOS App через local socket

Раньше я видел статью, которая рассказывала о пяти способах коммуникации между 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 (при отправке письма замените # на @) для подачи жалобы,并提供相关证据. Если подтвердится, что материал涉嫌侵权, сайт немедленно удалят涉嫌侵权的内容。

Рекомендуем также