English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
앱 간의 다섯 가지 통신 방법에 대해 소개한 한篇文章을 보았습니다. 각각의 방법은 URL Scheme, Keychain, UIPastedboard, UIDocumentInteractionController, 그리고 로컬 통신을 위한 소켓을 사용합니다. 이전4모두 사용했던 경험이 있습니다. 매우 간단하고, 몇 줄의 코드로 끝납니다. 마지막 것은 전에 사용한 적이 없었습니다. (아직도 초보자입니다. 용서해 주세요.), 오늘은 이를 시도해 보았습니다. 여기에 기록해 두고 함께 공유합니다.
잘못 말했습니다. 더 이상 말도 안 되는 소리를 하지 말고 시작해 보겠습니다:
먼저 원리에 대해 설명드리겠습니다. 사실 매우 간단합니다. 하나의 앱이 로컬 포트에서 TCP bind와 listen을 수행하고, 또 다른 앱이 동일한 포트에서 connect를 수행하면 정상적인 TCP 연결이 이루어지며, 전달하고 싶은 어떤 데이터든지 전달할 수 있습니다.서버 측을 먼저 생성하기 시작합니다:
1먼저 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본지 주소와 포트 번호를 바인딩합니다
// 주소 구조체 데이터, 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(@"socket 오류 : %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]);
3connect() 함수를 사용하여 호스트에 연결합니다
/* * connect 함수는 일반적으로 클라이언트가 TCP 연결을 초기화하며, 지정된 주소의 호스트에 연결합니다. 함수는 int 값을 반환합니다.-1실패 * 첫 번째 매개변수는 소켓 함수가 생성한 소켓으로, 이 소켓이 지정된 호스트에 연결할 것을 의미합니다 * 두 번째 매개변수는 소켓 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{ // 데이터를 받아서 서브 스레드에 두고 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#w에 주세요.3codebox.com(메일을 보내면, #을 @으로 변경해 주세요. 신고를 하고, 관련 증거를 제공해 주세요. 확인되면, 이 사이트는 즉시 저작권 침해 내용을 삭제합니다。)