English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Anteriormente, vi un artículo que hablaba de cinco formas de comunicación entre aplicaciones, que son URL Scheme, Keychain, UIPastedboard, UIDocumentInteractionController y la comunicación local utilizando socket. Antes de4Se ha utilizado en varios casos, y es relativamente simple, solo unos pocos códigos. Respecto al último, no lo he utilizado antes (perdón, soy un novato), así que hoy intenté escribirlo y lo registro aquí para compartir con todos.
Bien, sin más preámbulos, comencemos:
Primero, hablemos de su principio, que es muy simple, un App realiza el bind y listen en el puerto local, y otro App realiza el connect en el mismo puerto local, de esta manera se establece una conexión TCP normal, y se puede transmitir cualquier tipo de datos. Ahora comencemos creando el servidor:
1、primero usar la función socket() para crear un socket}}
/* * socket devuelve un valor int,-1por creación fallida * El primer parámetro especifica la familia de protocolo/dominio, generalmente hay AF_INET(IPV4)、AF_INET6)、AF_INET6)、AF_LOCAL * El segundo parámetro especifica un tipo de conector: SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET y otros * El tercer parámetro especifica el protocolo de transmisión correspondiente, como TCP/UDP y otros, generalmente se configura en 0 para usar este valor predeterminado */ int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1){ close(sock); NSLog(@"error de socket : %d",sock);<br> return; } /* * socket devuelve un valor int,-1por creación fallida * El primer parámetro especifica la familia de protocolo/dominio, generalmente hay AF_INET(IPV4)、AF_INET6)、AF_INET6)、AF_LOCAL * El segundo parámetro especifica un tipo de conector: SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET y otros * El tercer parámetro especifica el protocolo de transmisión correspondiente, como TCP/UDP y otros, generalmente se configura en 0 para usar este valor predeterminado */ int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1){ close(sock); NSLog(@"error de socket : %d",sock);<br> return; }
2、bindear la dirección y el número de puerto de la máquina
// Estructura de datos de dirección, que registra ip y número de puerto struct sockaddr_in sockAddr; // Declarar el protocolo utilizado sockAddr.sin_family = AF_INET; // Obtener la dirección IP de la máquina, convertida a tipo char const char *ip = [[self getIPAddress] cStringUsingEncoding:NSASCIIStringEncoding]; // Asignar ip a la estructura, inet_addr() es convertir una dirección IP de punto decimal a un entero de tipo largo sockAddr.sin_addr.s_addr = inet_addr(ip); // Configurar el número de puerto, htons() es convertir una variable de tipo entero de orden de bytes del host a orden de bytes de red sockAddr.sin_port = htons(12345; /* * La función bind se utiliza para asociar una dirección con el socket, devolviendo un valor int,-1为失败 * El primer parámetro especifica el socket, es decir, el socket devuelto por la llamada a la función socket anterior * El segundo parámetro especifica la dirección específica * El tercer parámetro especifica el tamaño de los datos de la dirección */ int bd = bind(sock,(struct sockaddr *) &sockAddr, sizeof(sockAddr)); if(bd == -1){ close(sock); NSLog(@"error de bind : %d",bd); return; }
3、监听绑定的地址
/* * La función listen cambia el conector de conexión activa en el conector de conexión pasiva, lo que permite aceptar solicitudes de otros procesos, devolviendo un valor int,-1为失败 * El primer parámetro es el socket devuelto por la función socket * El segundo parámetro se puede entender como el límite máximo de conexión */ int ls = listen(sock,20); if(ls == -1){ close(sock); NSLog(@"error de escucha : %d",ls); return; }
4、esperar la conexión del cliente, usar accept()(dado que la función accept() bloqueará el hilo, durante el proceso de espera de la conexión se quedará atascado, por lo que se recomienda colocarlo en un subproceso)
// 1.Abrir un subproceso NSTread *recvThread = [[NSThread alloc] initwithTarget:self selector:@selector(recvData) object: nil]; [recvThread start]; - (void)recvData{ // 2.Esperar la conexión del cliente // Declarar una estructura de dirección, que se utilizará para recibir la dirección devuelta por el cliente struct sockaddr_in recvAddr; // Tamaño de la dirección socklen_t recv_size = sizeof(struct sockaddr_in); /* * La función accept() devolverá un nuevo socket (self.newSock) después de que se establezca la conexión, que se utilizará para enviar y recibir datos con este cliente * El primer parámetro es el socket que estaba escuchando, que era una variable local, ahora necesita cambiarse a una variable global * El segundo parámetro es un parámetro de resultado, que se utiliza para recibir un valor de retorno, que especifica la dirección del cliente * El tercer parámetro también es un parámetro de resultado, que se utiliza para recibir el representante de la estructura recvAddr, que indica el número de bytes que ocupa */ self.newSock = accept(self.sock,(struct sockaddr *) &recvAddr, &recv_size); // 3.Llegar aquí significa que ya se ha conectado a un nuevo cliente, por lo que se puede comenzar a enviar y recibir datos, principalmente utilizando las funciones send() y recv() ssize_t bytesRecv = -1; // Tamaño de bytes devueltos char recvData[128] = ""; // Área de cache de datos devueltos // Si un extremo cierra la conexión, recv devolverá inmediatamente, bytesrecv será igual a 0, y luego el bucle while continuará ejecutándose, por lo que el juicio de igualdad a 0 es saltar fuera while(1){ bytesRecv = recv(self.newSocket,recvData,128,0); // recvData es los datos recibidos if(bytesRecv == 0){ break; } } }
5、enviar datos
- (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(@"socket error : %d",sock); return; }
2、获取主机的地址
NSString *host = [self getIPAddress]; // 获取本机ip地址 // 返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针 struct hostent *remoteHostEnt = gethostbyname([host UTF8String]); if(remoteHostEnt == NULL){ close(sock); NSLog(@"无法解析服务器主机名"); return; }// 配置套接字将要连接主机的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(@"Conexión fallida"); return; } NSLog("Conexión exitosa"); // Llegar aquí significa que se ha establecido la conexión;
4Después de que se establece la conexión, se puede enviar y recibir datos.
- (IBAction)senddata:(id)sender { // Enviar datos char sendData[32] = "hello service"; ssize_t size_t = send(self.sock, sendData, strlen(sendData), 0); NSLog(@"%zd",size_t); } - (void)recvData{ // Recibir datos, poner en el hilo secundario 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; } } }
Bueno, la comunicación entre dos aplicaciones locales utilizando socket es así. Es la primera vez que escribo un artículo, por un lado, para registrar mis reflexiones, y por otro lado, para compartir con todos. Espero que todos puedan señalar los errores que pueda haber en el texto. Finalmente, adjunto la dirección del Demo, dos proyectos, todos los que tengan interés pueden descargarlos e intentarlo:
Esto es todo el contenido del artículo, espero que ayude a su aprendizaje y que todos lo apoyen y alienten el tutorial de grito.
Declaración: El contenido de este artículo se obtiene de la red, es propiedad del autor original, el contenido se contribuye y carga espontáneamente por los usuarios de Internet, este sitio no posee los derechos de propiedad, no se ha procesado editorialmente y no asume ninguna responsabilidad legal. Si encuentra contenido sospechoso de copyright, por favor envíe un correo electrónico a: notice#oldtoolbag.com (al enviar un correo electrónico, reemplace # con @) para denunciar y proporcionar evidencia relevante. Una vez confirmado, este sitio eliminará inmediatamente el contenido sospechoso de infracción.