관리 메뉴

HAMA 블로그

소켓 (Stream 와 object-c ) 기반 iOS 프로그래밍 [번역] 본문

아이폰 (IOS)

소켓 (Stream 와 object-c ) 기반 iOS 프로그래밍 [번역]

[하마] 이승현 (wowlsh93@gmail.com) 2016. 11. 17. 17:18

https://www.zybuluo.com/Xiaojun-Jin/note/29874  원본

참고로 스위프트 3.0 기반으로 처음 개발하시는분들은 이걸 사용하는게 나을거 같습니다.

[Swift 3 ]  가장 쉬운 소켓 (TCP)  통신 방법을 찾고 계신가요? 

소켓 기반  iOS 프로그래밍 

소켓은 양방향으로 데이터를 전송할 수있는 도구입니다. 따라서 소켓은 양면을 가지며, 각면은 IP 주소와 포트라는 두 요소의 조합으로 식별됩니다.

데이터가 전송되는 방식 (프로토콜)이 다른 많은 종류의 소켓이 있습니다. 가장 많이 사용되는 유형은 TCP와 UDP 이며 . 이 튜토리얼에서는 TCP 소켓을 다루겠습니다.

iOS 프로젝트를 작성하기 전에 Python 언어를 사용하여 TCP 서버를 만들 계획입니다. 그러나 특정 서버 구현이 iOS 기술과 관련이 없으므로 여기서 건너 뛰고 서버가 준비 되었다고 가정합니다. 이제 App Client에 초점을 맞추고 소켓 프로그래밍을 해 보겠습니다.  클라이언트는 채팅 룸 가입, 메시지 보내기메시지 수신이라는 세 가지 주요 작업을 합니다.

Stream Programming

iOS에서 소켓 연결을 설정하기 위해 우리는 스트림을 사용합니다. 스트림은 데이터를 송수신하는 메커니즘에 대한 추상화입니다. 또한 스트림에는 "연결이 열려 있습니다", "데이터가 수신되었습니다", "연결이 닫혔습니다"등과 같은 특정 이벤트에 따라 대응할 수있는 대리자가 연결되어 있습니다.

Cocoa Framework에 포함 된 스트림과 관련된 중요한 클래스가 있습니다.

  • NSStream 
    This is the super class which defines some abstract features like open, close and delegate.
  • NSInputStream 
    A subclass of NSStream for reading input.
  • NSOutputStream 
    A subclass of NSSTream for writing output.

여기있는 유일한 문제는 NSStream 클래스가 원격 호스트에 연결할 수없는 반면 CFStream은 능력이 있다는 것입니다. 다행스럽게도 NSStream과 CFStream은 일종의 브리지 연결이므로 NSStream 형식의 CFStream을 쉽게 가져올 수 있습니다.

  1. NSInputStream *inputStream; NSOutputStream *outputStream;
  2. - (void)initNetworkCommunication
  3. {
  4. CFReadStreamRef readStream; CFWriteStreamRef writeStream;
  5. CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"localhost",
  6. 80, &readStream, &writeStream);
  7. inputStream = (NSInputStream *)readStream;
  8. outputStream = (NSOutputStream *)writeStream;
  9. [inputStream setDelegate:self]; [outputStream setDelegate:self];
  10. [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  11. [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  12. [inputStream open]; [outputStream open];
  13. }

CFStreamCreatePairWithSocketToHost는 두 스트림을 호스트와 포트에 바인딩하는 데 사용됩니다. 일단 호출하면 CFStream을 NSStream으로 자유롭게 캐스팅 할 수 있습니다.

스트림은 지속적으로 데이터를 보내거나받을 준비가되어 있어야합니다. 이 기능을 사용하려면 실행 루프에서 이벤트를 수신하도록 스트림을 예약해야합니다. 앱은 이벤트를 스트리밍하는 데 반응해야하지만 이벤트의 자비가되지는 않습니다. 실행 루프 스케줄링을 사용하면 다른 코드 (필요한 경우)를 실행할 수 있지만 스트림에서 문제가 발생하면 알림을받을 수 있습니다.

Joining the Chat

서버에 연결되면 채팅에 참여할 준비가되었습니다. 조인 메시지의 형식은 "iam : name"입니다. 그래서 우리는 문자열을 만들어서 outputStream에 써야합니다.

  1. - (IBAction)joinChat:(id)sender
  2. {
  3. NSString *res = [NSString stringWithFormat:@"iam:%@", inputNameField.text];
  4. NSData *dt = [[NSData alloc] initWithData:[res dataUsingEncoding:NSASCIIStringEncoding]];
  5. [outputStream write:[data bytes] maxLength:[dt length]];
  6. }

이벤트를 전달하기 위해 서버는 클라이언트 / 서버가주고받는 데이터의 예상 포맷 / 시퀀스를 정의하는 프로토콜을 정의해야합니다. 이 응용 프로그램에서는 매우 간단한 문자열 기반 프로토콜을 사용합니다. iammeans "사용자가 채팅에 참여했습니다."msg는 "메시지 보내기"를 나타냅니다.

Sending Messages

채팅에 참여하는 것과 매우 유사한 방식으로이를 구현합니다. "iam :"을 "msg :"로 전환하면됩니다.

  1. - (IBAction)sendMessage:(id)sender
  2. {
  3. NSString *res = [NSString stringWithFormat:@"msg:%@", inputMessageField.text];
  4. NSData *dt = [[NSData alloc] initWithData:[res dataUsingEncoding:NSASCIIStringEncoding]];
  5. [outputStream write:[data bytes] maxLength:[dt length]];
  6. }

Receiving Messages

애플리케이션이 서버에서 메시지를 수신하고 있다는 사실을 알리려면 NSStream 대리자 스트림 인 handleEvent를 구현해야합니다. 그러면 스트림에서 발생하는 활동에 Google 애플리케이션이 반응 할 수 있습니다.

  1. - (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
  2. {
  3. NSLog(@"stream event %i", streamEvent);
  4. switch (streamEvent)
  5. {
  6. case NSStreamEventOpenCompleted:
  7. NSLog(@"Stream opened");
  8. break;
  9. case NSStreamEventHasBytesAvailable: // fundamental to receive messages
  10. if (theStream == inputStream)
  11. {
  12. uint8_t buffer[1024]; int len;
  13. while ([inputStream hasBytesAvailable])
  14. {
  15. len = [inputStream read:buffer maxLength:sizeof(buffer)];
  16. if (len > 0)
  17. {
  18. NSString *output = [[NSString alloc] initWithBytes:buffer
  19. length:len
  20. encoding:NSASCIIStringEncoding];
  21. if (nil != output)
  22. {
  23. NSLog(@"server said: %@", output);
  24. }
  25. }
  26. }
  27. }
  28. break;
  29. case NSStreamEventErrorOccurred:
  30. NSLog(@"Can not connect to the host!");
  31. break;
  32. case NSStreamEventEndEncountered:
  33. [theStream close];
  34. [theStream removeFromRunLoop:[NSRunLoop currentRunLoop]
  35. forMode:NSDefaultRunLoopMode];
  36. [theStream release];
  37. theStream = nil;
  38. break;
  39. default:
  40. NSLog(@"Unknown event");
  41. }
  42. }

while 루프를 사용하여 스트림의 바이트를 수집합니다. 스트림에 아무것도 남아 있지 않으면 read 메소드는 0을 리턴합니다. 결과가 0보다 큰 경우 버퍼를 String으로 변환하고 결과를 출력합니다.

기기에서 앱 실행 :

먼저 "localhost"문자열을 컴퓨터의 ip로 전환하십시오. 현재 IP를 찾으려면 "시스템 환경 설정> 네트워크"로 가십시오. 장치는 무선으로 컴퓨터를 서비스하는 동일한 라우터에 연결해야합니다. 3G 연결을 사용하려면 포트 80에서 네트워크 외부의 연결을 허용하도록 라우터를 구성해야합니다 (권장하지 않음).


Comments