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

Коммуникация между iOS App через local socket

I saw an article that introduced five ways of communication between Apps, they are URL Scheme, Keychain, UIPastedboard, UIDocumentInteractionController, and using socket for local communication. The first four have been used before and are relatively simple, just a few lines of code. For the last one, I haven't used it before (forgive me, I'm still a beginner), so I tried writing it today, and I'm recording it here to share with everyone. 

Alright, no more chatter, let's get started: 

First, let's talk about its principle, it's actually very simple, one App binds and listens on a local port for TCP, and another App connects to the same port locally, thus establishing a normal TCP connection, you can send any data you want. Let's start by creating the server side: 

1. First, create a socket using the socket() function 

/*
* Socket returns an int value, -1 indicates failure to create
* Первый параметр indicates the protocol family/domain, usually AF_INET(IPV4), AF_INET6(IPV6), AF_LOCAL
* Второй параметр specifies a socket type: SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET, etc.
* Третий параметр specifies the corresponding transport protocol, such as TCP/UDP, usually set to 0 to use this default value
*/
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1){
close(sock);
NSLog(@"ошибка сокета : %d", sock);<br> return;
}
/*
 * Socket returns an int value, -1 indicates failure to create
 * Первый параметр indicates the protocol family/domain, usually AF_INET(IPV4), AF_INET6(IPV6), AF_LOCAL
 * Второй параметр specifies a socket type: SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET, etc.
 * Третий параметр specifies the corresponding transport protocol, such as TCP/UDP, usually set to 0 to use this default value
 */
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];
// Assign ip к структуре, inet_addr() это функция, которая преобразует десятичное число в долгое целое
sockAddr.sin_addr.s_addr = inet_addr(ip);
// Устанавливаем номер порта, htons() это преобразование целого числа от порядка bytes в сетевом порядке
sockAddr.sin_port = htons(12345);
/*
 * Функция bind используется для привязки адреса к socket, возвращает значение int, -1 в случае неудачи
 * Первый параметр specifies socket, это socket, возвращенный функцией socket
 * Второй параметр - это указанное адрес
 * Третий параметр - это размер адресных данных
 */
int bd = bind(sock,(struct sockaddr *) &sockAddr, sizeof(sockAddr));
if(bd == -1){
 close(sock);
 NSLog(@"ошибка привязки: %d",bd);
 return;
}

3. Слушаем связанный адрес

/*
 * Функция listen использует активный socket для подключения в пассивный, что позволяет принимать запросы от других процессов и возвращает значение int, -1 в случае неудачи
 * Первый параметр - этоsocket, возвращенный функцией socket
 * Второй параметр можно理解为 максимальное ограничение соединений
 */
int ls = listen(sock,20);
if(ls == -1){
 close(sock);
 NSLog(@"ошибка прослушивания: %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(@"Не удалось解析 имя хоста сервера");
 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, все это и нужно. Это мой первый блог, я пишу его, чтобы записать свои мысли и поделиться ими с вами. Если в статье есть ошибки, пожалуйста, указывайте их. В конце я добавлю адрес Demо, два проекта, если у вас есть интерес, вы можете их загрузить и попробовать:

Вот и все, что есть в этой статье, я надеюсь, что это поможет вам в изучении. И希望大家多多支持呐喊教程。

Заявление: контент этой статьи был получен из Интернета, авторские права принадлежат соответствующему автору. Контент был предоставлен пользователями Интернета в добровольном порядке и загружен самостоятельно. Этот сайт не обладает правами собственности на него, не был отредактирован вручную и не несет ответственности за него. Если вы обнаружите контент,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма замените # на @) для сообщения о нарушении и предоставьте соответствующие доказательства. Если будет установлено, что материал нарушил права на интеллектуальную собственность, сайт незамедлительно удалят涉嫌侵权的内容。

Вам может понравиться