iOS socket 네트워크 프로 그래 밍 실례 상세 설명

코드 다운로드
서버 코드 다운로드 주소
클 라 이언 트 코드 다운로드 주소
관련 개념
socket 은 TCP 와 UDP 프로 그래 밍 을 위 한 인터페이스 입 니 다.이 를 통 해 TCP 연결 등 을 만 들 수 있 습 니 다.socket 은 TCP/IP 프로 토 콜 에 대한 패키지 입 니 다.Socket 자 체 는 프로 토 콜 이 아니 라 호출 인터페이스(API)입 니 다.Socket 을 통 해 TCP/IP 프로 토 콜 을 사용 할 수 있 습 니 다.Socket 의 등장 은 프로그래머 로 하여 금 TCP/IP 프로 토 콜 스 택 을 더욱 편리 하 게 사용 하 게 할 뿐 TCP/IP 프로 토 콜 에 대한 추상 적 인 것 으로 우리 가 알 고 있 는 가장 기본 적 인 함수 인 터 페 이 스 를 형성 했다.
socket 연결:socket 연결 은 이른바 긴 연결 입 니 다.이론 적 으로 클 라 이언 트 와 서버 측 이 연결 을 구축 하면 주동 적 으로 끊 기지 않 습 니 다.그러나 각종 환경 요소 로 인해 연결 이 끊 길 수 있 습 니 다.예 를 들 어 서버 쪽 이나 클 라 이언 트 호스트 다운,네트워크 고장,또는 이들 사이 에 데이터 전송 이 장시간 없 으 면 네트워크 방화벽 은 이 연결 을 끊 어 네트워크 자원 을 방출 할 수 있 습 니 다.따라서 socket 연결 에 데이터 전송 이 없 으 면 연결 을 유지 하기 위해 심장 박동 메 시 지 를 보 내야 합 니 다~구체 적 인 심장 박동 메시지 형식 은 개발 자가 정의 합 니 다.
TCP 로 연 결 된 세 번 의 악수:
첫 악수:클 라 이언 트 가 syn 패키지(syn=j)를 서버 에 보 내 고 SYN 에 들 어 갑 니 다.SEND 상태,서버 확인 대기;
두 번 째 악수:서버 는 syn 가방 을 받 았 습 니 다.고객 의 SYN(ack=j+1)을 확인 하고 자신 도 SYN 가방(syn=k),즉 SYN+ACK 가방 을 보 내야 합 니 다.이때 서버 는 SYN 에 들 어 갑 니 다.RECV 상태;
세 번 째 악수:클 라 이언 트 가 서버 의 SYN+ACK 패 키 지 를 받 아 서버 에 확인 팩 ACK(ack=k+1)를 보 냅 니 다.이 패 키 지 를 보 내 면 클 라 이언 트 와 서버 가 ESTABLISHED 상태 에 들 어가 세 번 의 악 수 를 완성 합 니 다.
악수 과정 에서 전 송 된 가방 에는 데이터 가 포함 되 어 있 지 않 고 세 번 의 악수 가 끝 난 후에 야 클 라 이언 트 와 서버 는 본 격 적 으로 데 이 터 를 전송 하기 시작 했다.이상 적 인 상태 에서 TCP 연결 이 구축 되면 통신 쌍방 중 어느 한 측 이 자발적으로 연결 을 닫 기 전에 TCP 연결 은 계속 유 지 됩 니 다.연결 을 끊 을 때 서버 와 클 라 이언 트 는 TCP 연결 을 끊 어 달 라 는 요청 을 자발적으로 할 수 있 습 니 다.끊 는 과정 은'네 번 의 악수'를 거 쳐 야 합 니 다.
socket 네트워크 연결 구축 절차
Socket 연결 을 만 들 려 면 최소한 한 쌍 의 소켓 이 필요 합 니 다.그 중 하 나 는 클 라 이언 트 에서 실 행 됩 니 다.클 라 이언 트 Socket 이 라 고 하고 다른 하 나 는 서버 에서 실 행 됩 니 다.ServerSocket 이 라 고 합 니 다.
소켓 간 의 연결 과정 은 서버 감청,클 라 이언 트 요청,연결 확인 세 단계 로 나 뉜 다.
1。서버 감청:서버 엔 드 소켓 은 구체 적 인 클 라 이언 트 소켓 을 찾 지 않 고 연결 을 기다 리 는 상태 에 있 습 니 다.네트워크 상 태 를 실시 간 으로 감시 하고 클 라 이언 트 의 연결 요청 을 기다 리 고 있 습 니 다.
2。클 라 이언 트 요청:클 라 이언 트 의 소켓 이 연결 요청 을 하 는 것 을 말 합 니 다.연결 할 목 표 는 서버 엔 드 의 소켓 입 니 다.이 를 위해 클 라 이언 트 의 소켓 은 연결 할 서버 의 소켓 을 먼저 설명 하고 서버 엔 드 소켓 의 주소 와 포트 번 호 를 지적 한 다음 에 서버 엔 드 소켓 에 연결 요청 을 해 야 합 니 다.
3。연결 확인:서버 측 소켓 이 클 라 이언 트 소켓 의 연결 요청 을 듣 거나 받 았 을 때 클 라 이언 트 소켓 의 요청 에 응 하여 새로운 스 레 드 를 만 들 고 서버 측 소켓 의 설명 을 클 라 이언 트 에 보 냅 니 다.클 라 이언 트 가 이 설명 을 확인 하면 쌍방 은 정식 적 으로 연결 을 만 듭 니 다.한편,서버 엔 드 소켓 은 계속 감청 상태 에 있 고 다른 클 라 이언 트 소켓 의 연결 요청 을 계속 받 습 니 다.
BSD socket
BSD socket:완전히 c 언어 로 구현 되 며 Objective-C 코드 에서 사용 할 수 있 습 니 다.
장점:서로 다른 플랫폼 에서 이식 하기 쉽다.
단점:
운영 체제 에 설 치 된 네트워크 특성(예 를 들 어 시스템 범위 의 VPN)에 접근 할 수 없습니다.
더 나 쁜 것 은 socket 연결 을 초기 화하 면 장치 의 와 이 파이 나 벌집 네트워크 가 자동 으로 열 리 지 않 습 니 다.무선 네트워크 는 배터리 전 기 를 절약 하기 위해 스마트 하 게 닫 히 고 모든 통신 연결 이 실패 합 니 다.다른 네트워크 프로 세 스 가 무선 네트워크 를 활성화 하지 않 는 한.
CFNetwork 는 BSD Socket 에 대한 분장 으로 장치 의 무선 네트워크 를 활성화 시 킬 수 있 기 때문에 거의 모든 장면 에서 BSD Socket 이 아 닌 CFNetwork 를 사용 하 는 것 을 권장 합 니 다.
가장 많이 사용 되 는 API:
1.int socket(int,int,int):새 socket 을 만 들 고 초기 화 합 니 다.파일 설명 자 를 성공 적 으로 놓 으 면 0 으로 돌아 갑 니 다.
2.int bind(int, const struct sockaddr *, socklen_t):지정 한 주소 와 포트 번호 에 socket 을 할당 합 니 다.
3.int listen(int, int) __DARWIN_ALIAS(listen):서버 감청 클 라 이언 트 에 사용
4.int accept(int, struct sockaddr * __restrict, socklen_t * __restrict):연결 요청 을 받 아들 여 클 라 이언 트 주 소 를 clientAddress 에 저장 합 니 다.
5.int connect(int, const struct sockaddr *, socklen_t):지정 한 서버 에 연결 합 니 다.
6.ssize_t send(int, const void *, size_t,int):socket 에서 최대 XX 데 이 터 를 보 냅 니 다.
7.ssize_t recv(int, void *, size_t,int):socket 에서 최대 XX 데 이 터 를 읽 습 니 다.
……
서버 논리:클 라 이언 트 가 보 내 는"1"을 받 으 면"맞 춰 봐!"라 고 대답 합 니 다.클 라 이언 트 가 보 내 왔 습 니 다."2"그럼 답장"감사합니다!"클 라 이언 트 가'3'을 보 내 왔 습 니 다.그러면 답장'이 맞지 않 습 니 다!'클 라 이언 트 가"4"를 보 내 면"네!"라 고 대답 합 니 다.클 라 이언 트 가 다른 내용 을 보 내 왔 습 니 다.그러면 반 확률 로 그대로 답장 하고 반 확률 로 답장 합 니 다."무슨 말 인지 모 르 겠 습 니 다!"
屏幕快照 2017-03-16 下午2.16.02.png
서버 생 성:
1.소켓 만 들 기

struct sockaddr_in server_addr;
 server_addr.sin_len = sizeof(struct sockaddr_in);
 server_addr.sin_family = AF_INET;//AF_INET      
 server_addr.sin_port = htons(port);
 server_addr.sin_addr.s_addr = inet_addr(address);
 bzero(&server_addr.sin_zero, 8);
 //  socket
 int server_socket = socket(AF_INET, SOCK_STREAM, 0);//SOCK_STREAM    
 if (server_socket == -1) {
  perror("socket error!");
  return 1;
 }
2.바 인 딩 socket:생 성 된 socket 을 로 컬 IP 주소 와 포트 에 연결 합 니 다.이 socket 은 반 관련 이 있 습 니 다.클 라 이언 트 의 연결 요청 만 수사 하고 클 라 이언 트 와 통신 할 수 없습니다.

 int bind_result = bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
 if (bind_result == -1) {
  perror("bind error!");

  return 1;
 }
3.도청 수사

 if (listen(server_socket, 5)) {
  perror("listen error!");
  return 1;
 }
4.클 라 이언 트 연결 받 기

int client_socket;
 socklen_t address_len;
 struct sockaddr_in client_address;
 for (; ; ) {
  address_len = sizeof(client_address);
  client_socket = accept(server_socket, (struct sockaddr*)&client_address, &address_len);
  //1.     
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   //          
   str_echo(client_socket);
  });
 }
5.데이터 수신 및 발송

 char buf[1024];
 while (1) {
  bzero(buf, 1024);
  long byte_num = recv(socket, buf, 1024, 0);
  if (byte_num < 0) {
   return;
  }
  buf[byte_num] = '\0';
  printf("client said:%s
", buf); char *result; NSString *str = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding]; if ([str isEqualToString:@"1"]) { result = " !"; } else if ([str isEqualToString:@"2"]) { result = " !"; } else if ([str isEqualToString:@"3"]) { result = " !"; } else if ([str isEqualToString:@"4"]) { result = " !"; } else { if (arc4random()%2 == 0) { result = " !"; } else { result = buf; } } send(socket, result, 1024, 0); }
클 라 이언 트 생 성:

struct sockaddr_in server_addr;
 server_addr.sin_len = sizeof(struct sockaddr_in);
 server_addr.sin_family = AF_INET;
 server_addr.sin_port = htons(11332);
 server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
 bzero(&(server_addr.sin_zero), 8);
 int server_socket = socket(AF_INET, SOCK_STREAM, 0);
 if (server_socket == -1) {
  perror("socket error");
  return 1;
 }
 char receive_msg[1024];
 char reply_msg[1024];
 if (connect(server_socket, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_in)) == 0) {
  //connect     ,         socket             ,      ,         ,             。
  while (1) {
   bzero(reply_msg, 1024);
   printf("replay:");
   scanf("%s", reply_msg);
   if (send(server_socket, reply_msg, 1024, 0) == -1) {
    perror("send error!");
    return 1;
   }
   bzero(receive_msg, 1024);
   long byte_num = recv(server_socket, receive_msg, 1024, 0);
   receive_msg[byte_num] = '\0';
   printf("server said:%s
", receive_msg); } } return 0;
NSStream 읽 기 및 쓰기 파일
Cocoa Streams 는 NSStream,NSInputStream,NSOutputStream 등 세 가지 관련 종 류 를 포함한다.
NSStream:추상 적 인 유형 으로 기본 적 인 속성 과 방법 을 정의 했다.
NSInputStream:NSStream 의 하위 클래스 로 NSData,File,socket 에서 데이터 흐름 을 읽 을 수 있 습 니 다.
NSOutputStream:NSStream 의 하위 클래스 이기 도 합 니 다.이 를 통 해 데 이 터 를 NSData,File,socket 에 기록 할 수 있 습 니 다.
stream 대상 에 Delegate(NSStreamDelegate)를 설정 할 수도 있 으 며,stream 에 Delegate 를 정확하게 지정 하지 않 으 면 기본적으로 Delegate 를 자신 으로 설정 합 니 다.
NSStream Delegate 는 방법 이 하나 밖 에 없어 요.
(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
NSStreamEventNone:기본 값 을 바 꾸 는 것 은 이벤트 가 아 닙 니 다.
NSStreamEventOpenComplete:소켓 이 성공 적 으로 열 렸 습 니 다.
NSStreamEvent HasBytesAvailable:읽 을 수 있 는 바이트 가 있 습 니 다.
NSStreamEventHasSpaceAvailable:캐 시 에 바이트 를 쓸 수 있 습 니 다.
NSStreamEventError Occurred:작업 에 오류 가 발생 했 습 니 다.CFReadStreamCopyError()와 CFWrite StreamCopyError()는 더 많은 오류 디 테 일 을 제공 합 니 다.
NSStreamEventEndEncountered:socket 이 바이트 흐름 의 끝 에 도달 합 니 다.
NSStream 대상 을 통 해 데 이 터 를 읽 으 면 다음 과 같은 절차 로 나 눌 수 있 습 니 다.
a)NSStream 대상 을 데이터 원본 에서 만 들 고 초기 화 합 니 다.
b)run loop 을 설정 하고 stream 대상 을 엽 니 다.
c)NSInputStream 이벤트(NSStreamDelegate)에 응답 합 니 다.
d)NSInputStream 대상 을 닫 습 니 다.

- (void)viewDidLoad {
 [super viewDidLoad];
 NSString *documentStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
 NSString *dataPath = [documentStr stringByAppendingPathComponent:@"data.txt"];
 NSFileManager *manager = [NSFileManager defaultManager];
 if ([manager fileExistsAtPath:dataPath]) {
  self.read = YES;
  NSLog(@"string:%@", [NSString stringWithContentsOfFile:dataPath encoding:NSUTF8StringEncoding error:nil]);
  NSInputStream *inStream = [[NSInputStream alloc] initWithFileAtPath:dataPath];
  [inStream setDelegate:self];
  [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
  [inStream open];
 }
 else
 {
  if ([manager createFileAtPath:dataPath contents:nil attributes:nil]) {
   self.read = NO;
   NSOutputStream *outStream = [[NSOutputStream alloc] initToFileAtPath:dataPath append:YES];
   [outStream setDelegate:self];
   [outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
   [outStream open];
  }
  else
  {
   NSLog(@"      !");
  }
 }
}
#pragma mark - <NSStreamDelegate>    
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
 if (self.read) {
  NSInputStream *inStream = (NSInputStream *)aStream;
  switch (eventCode) {
   case NSStreamEventHasBytesAvailable:
   {
    uint8_t data[1024];
    [inStream read:data maxLength:1024];
    printf("%s", data);
    [inStream close];
    [inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    inStream = nil;
   }
    break;
   case NSStreamEventEndEncountered:
   {
    [inStream close];
    [inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    inStream = nil;
   }
    break;
   default:
    break;
  }
 }
 else{
  NSOutputStream *outStream = (NSOutputStream *)aStream;
  switch (eventCode) {
   case NSStreamEventHasSpaceAvailable:
   {
    uint8_t data[] = "{name:'  ', age:10}";
    [outStream write:data maxLength:strlen((char *)data)];
    [outStream close];
    [outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    outStream = nil;
   }
    break;
   case NSStreamEventEndEncountered:
   {
    [outStream close];
    [outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    outStream = nil;
   }
    break;
   default:
    break;
  }
 }
}
NSStream Socket
NSStream 클래스 는 IOS 플랫폼 에서 socket 연결 을 만 드 는 것 을 지원 하지 않 으 며,CFStream 은 IOS 플랫폼 에서 socket 행 위 를 지원 합 니 다.따라서 원 격 호스트 의 DNS 나 IP 주 소 를 알 고 있다 면 CFStreamCreate Pair With SockettToHost 함 수 를 사용 하여 socket 연결 을 만 들 수 있 습 니 다.이 함 수 를 통 해 CFStream 형식 이 듀 플 렉 스 인 socket 연결 을 만 들 었 고,이 어 toll-free bridge 를 이용 하여 CFStream 대상 을 NSStream 대상 으로 바 꿀 수 있 습 니 다.
NSStream 대상 을 통 해 Socket 통신 을 하 는 것 은 NSStream 을 통 해 IO 작업 을 하 는 절차 와 거의 같 습 니 다.
a)NSStream 대상 을 만 들 고 CFStreamCreate Pair With SocketToHost 함 수 를 통 해 CFReadStreamRef 와 CFWrite StreamRef 대상 을 만 듭 니 다.이 어 두 가 지 를 NSInputStream 과 NSOutputStream 대상 으로 변환 합 니 다.
b)run loop 을 설정 하고 NSInputStream 과 NSOutputStream 대상 을 엽 니 다.
c)이벤트 에 응답 하고 Delegate 에서 서로 다른 신호 에 응답 합 니 다.
d)NSStream 대상 을 닫 습 니 다.
서버:서버 는 여전히 상기 예 의 서버 를 사용 합 니 다.
클 라 이언 트 의 실현:1~5 이 다섯 단 추 를 누 르 면 각각 서버 에서 서로 다른 데 이 터 를 되 돌려 보 여 줍 니 다.
屏幕快照 2017-03-16 下午3.55.49.png
실현 이 상대 적 으로 간단 하기 때문에 설명 을 하지 않 고 실현 코드 는 다음 과 같다.

- (void)viewDidLoad {
 [super viewDidLoad];
 [self startSocket:@"127.0.0.1" andPort:11332];
}
- (void)startSocket:(NSString *)address andPort:(int)port
{
 CFReadStreamRef readRef;
 CFWriteStreamRef writeRef;
 CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)address, port, &readRef, &writeRef);
 NSInputStream *inputStream = (__bridge NSInputStream *)readRef;
 NSOutputStream *outputStream = (__bridge NSOutputStream *)writeRef;
 inputStream.delegate = self;
 outputStream.delegate = self;
 [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
 [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
 [inputStream open];
 [outputStream open];
 self.outputStream = outputStream;
}
- (IBAction)sendMsg:(UIButton *)sender {
 if (sender.currentTitle > 0) {
  const char *output = sender.currentTitle.UTF8String;
  [self.outputStream write:(const uint8_t *)output maxLength:strlen(output)];
 }
}
- (void)showMessage:(NSString *)msg
{
 if (!self.msgLabel) {
  UIFont *font = [UIFont systemFontOfSize:14];
  UIColor *color = [UIColor blackColor];
  UIColor *backColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.2];
  UILabel *label = [[UILabel alloc] init];
  label.textColor = color;
  label.font = font;
  label.textAlignment = NSTextAlignmentCenter;
  label.backgroundColor = backColor;
  self.msgLabel = label;
 }
 CGFloat screenW = [UIScreen mainScreen].bounds.size.width;
 CGFloat screenH = [UIScreen mainScreen].bounds.size.height;
 CGFloat W = screenW - 16;
 CGSize size = [msg boundingRectWithSize:CGSizeMake(W, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.msgLabel.font} context:nil].size;
 W = size.width;
 CGFloat H = size.height;
 CGFloat X = (screenW - W)/2;
 CGFloat Y = (screenH - H)/2;
 self.msgLabel.frame = CGRectMake(X, Y, W, H);
 self.msgLabel.text = msg;
 [self.view addSubview:self.msgLabel];
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  [self.msgLabel removeFromSuperview];
 });
}
#pragma mark - <NSStreamDelegate>    
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
 switch (eventCode) {
  case NSStreamEventNone:
   break;
  case NSStreamEventOpenCompleted:
   break;
  case NSStreamEventHasBytesAvailable:
  {
   uint8_t buf[1024];
   NSInteger len = 0;
   NSInputStream *inputStream = (NSInputStream *)aStream;
   len = [inputStream read:buf maxLength:1024];
   if (len) {
    [self showMessage:[NSString stringWithCString:(const char *)buf encoding:NSUTF8StringEncoding]];
   }
   break;
  }
  case NSStreamEventHasSpaceAvailable:
   break;
  case NSStreamEventErrorOccurred:
  {
   [aStream close];
   break;
  }
  case NSStreamEventEndEncountered:
  {
   [aStream close];
   [aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
   aStream = nil;
   break;
  }
  default:
   break;
 }
위 에서 말 한 것 은 편집장 이 소개 한 iOS socket 네트워크 프로 그래 밍 입 니 다.여러분 에 게 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 저 에 게 메 시 지 를 남 겨 주세요.편집장 은 즉시 큰 답 을 해 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기