본문 바로가기
아이폰 개발/ios 개념&튜토리얼

iOS NSStream 이용한 채팅앱 예제

by 인생여희 2021. 3. 17.

iOS NSStream 이용한 채팅앱 예제

 

 

 

 

✅ iOS 네이티브 소스

 

SocketSessionViewController.h

 

#import <UIKit/UIKit.h>

@interface SocketSessionViewController : UIViewController  <NSStreamDelegate>

@end

 

 

 

 

SocketSessionViewController.m

 

#import "SocketSessionViewController.h"

@interface SocketSessionViewController ()
//텍스트 필드
@property (weak, nonatomic) IBOutlet UITextField *idTextField;

@property (weak, nonatomic) IBOutlet UIView *sessionView;

@property (strong, nonatomic) NSMutableArray *messages;

@property (weak, nonatomic) IBOutlet UITextView *returnedDataTextField;


@property (strong, nonatomic) IBOutlet UIButton *closeStream;

- (IBAction)joinSessionButtonAction:(id)sender;

@end

//스트림 객체
NSInputStream *inputStream;
NSOutputStream *outputStream;

@implementation SocketSessionViewController

//스트림 닫기
- (IBAction)closeStreamAction:(id)sender {
    
    NSLog(@"스트림 닫기");
    [inputStream close];
    [outputStream close];
}


- (void)viewDidLoad {
    [super viewDidLoad];
    
    //스트림 초기화
    [self initNetworkCommunication];
    _messages = [[NSMutableArray alloc]init];
    self.idTextField.text = @"";
    self.returnedDataTextField.text = @"";
}

- (void)initNetworkCommunication {
    
    /*
     여기있는 유일한 문제는 NSStream 클래스가 원격 호스트에 연결할 수없는 반면 CFStream은 가능하다!
     NSStream과 CFStream은 일종의 브리지 연결이므로 NSStream 형식의 CFStream을 쉽게 가져올 수 있다.
     */
    
    //원격 호스트에 연결 가능
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    
    //특정 호스트의 TCP / IP 포트에 연결된 읽기 및 쓰기 가능한 스트림을 생성
    //두 스트림을 호스트와 포트에 바인딩하는 데 사용. 일단 호출하면 CFStream을 NSStream으로 자유롭게 캐스팅 가능하다.

    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"172.20.10.4", 6666, &readStream, &writeStream);
    
    // NSStream ==> CFStream 캐스팅
    inputStream = (__bridge NSInputStream *)readStream;
    outputStream = (__bridge NSOutputStream *)writeStream;
    
    //스트림 델리게이트 설정
    [inputStream setDelegate:self];
    [outputStream setDelegate:self];
    
    /*
     스트림은 지속적으로 데이터를 보내거나받을 준비가되어 있어야 한다.
     이 기능을 사용하려면 실행 루프에서 이벤트를 수신하도록 스트림을 예약해야한다.
     NSRunLoop 스케줄링을 사용하면 다른 코드 (필요한 경우)를 실행할 수 있지만 스트림에서 문제가 발생하면 알림을받을 수 있습니다.
     */
    
    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    
    
    //스트림 열기
    [inputStream open];
    [outputStream open];
}

//쓰기
- (IBAction)joinSessionButtonAction:(id)sender {
    
    NSString *response = [NSString stringWithFormat:@"%@\n",self.idTextField.text];
    // NSString 을 UTF8 로 변환해서 raw stream 으로 쓴다.
    const uint8_t * rawstring = (const uint8_t *)[response UTF8String];
    
    //스트림에 쓰기
    [outputStream write:rawstring maxLength:strlen((const char *)rawstring)];

    self.idTextField.text = @"";
}

//스트림 델리게이트 메소드
-(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
    
    switch (streamEvent) {
            
        //쓰기 콜백
        case NSStreamEventHasSpaceAvailable: {
            
              if(theStream == outputStream) {
                  NSLog(@"outputStream is ready.");
              }
            
            break;
        }
        //읽기 콜백
        case NSStreamEventHasBytesAvailable:
            
            NSLog(@"inputStream is ready.");
            
            if (theStream == inputStream) {
                
                uint8_t buffer[1024];
                long len;
                
                while ([inputStream hasBytesAvailable]) {
                    
                    len = [inputStream read:buffer maxLength:sizeof(buffer)];
                    if (len > 0) {
                        
                        NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
                        
                        
                        
                        if (nil != output) {
                            NSLog(@"서버에서 받은 데이터: %@", output);
                            [self messageRevieved:output];
                        }
                    }
                }
            }
            break;

            
        //인풋, 아웃풋 스트림 열기
        case NSStreamEventOpenCompleted:
            NSLog(@"Stream 열었음.");
            break;
            
        //연결 실패
        case NSStreamEventErrorOccurred:
            NSLog(@"host에 연결 할 수 없습니다.");
            break;
            
        case NSStreamEventEndEncountered:
            NSLog(@"NSStreamEventEndEncountered");
            break;
            
        default:
            NSLog(@"Unknown event");
    }
    
}

//받은 메시지 처리
-(void)messageRevieved:(NSString *)message {
    
    NSLog(@"messageRevieved - 진입");
    
    NSMutableString *messagesString = [NSMutableString stringWithString:self.returnedDataTextField.text];
    
    [messagesString appendString:message];
    
    self.returnedDataTextField.text = messagesString;
    

}

@end

 

소스

SocketBuddy-master.zip
0.08MB

 

 

 

테이블 뷰 관련 소스 

 

Chat.zip
0.13MB

 

 

 

 

 

Java Server Socket 소스

 

package zzzz;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		
		System.out.println("start main");

		System.out.println("Welcome to the world of Socket");
		
		//서버소켓 
		ServerSocket serverSocket = null;

		try {
			serverSocket = new ServerSocket(6666);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}


		int i = 0;
		
		while (true) {
			Socket thisClient = null;
			PrintWriter pw = null;
			
			try {

				System.out.print("connection requests from clients");
				
				// accept 메소드는 프로세스를 차단합니다.
				thisClient = serverSocket.accept();
				
				System.out.println("There are a client socket requests consent.");
				
				//연결 - 소켓이 열려있다면
				while (!thisClient.isClosed()) {
					
					// 인풋 스트림 소켓 읽기
					InputStream is = thisClient.getInputStream();
					//클라이언트가 보낸 내용
					InputStreamReader clientInput = new InputStreamReader(is);
					//버퍼로 변환
					BufferedReader br = new BufferedReader(clientInput);
					//ip
					System.out.println("current socket client's IP address:" + thisClient.getInetAddress());
					//클라이언트가 보낸 내용
					String clientStr = br.readLine();
					//출력
					System.out.println("the client said:" + clientStr);
					
					
					//값이 있다면 
					if(clientStr != null) {
						
						// 클라이언트에 문자열 전송
						pw = new PrintWriter(thisClient.getOutputStream());
						pw.println(clientStr);
						pw.flush();	
						
						//연결 종료
						if (clientStr.equals("goodbye")) {
							
							System.out.println("연결종료....1");
							
							thisClient.close();
						}
						
					}else {
						System.out.println("연결종료....2");
						
						thisClient.close();
					}
					
				}

			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			// Read Input process flow will block the main process
			i++;

		}
		
		
		
	}

}

 

✅ Java  Client 소스 예제

 

public class startgo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		
		String ip = "172.20.10.4";
        int port = 5555;
        new Client1(ip, port);
		
		
	}

}

 

 

package zzz22clinet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class Client1 {
	private Socket socket;
	private BufferedReader br;
	private PrintWriter pw;
	
	public Client1(String ip, int port) {
		try {
			// 서버에 요청 보내기
			socket = new Socket(ip, port);
			System.out.println(socket.getInetAddress().getHostAddress() + "에 연결됨");
			
			// 메시지 받기
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pw = new PrintWriter(socket.getOutputStream());
			
			// 메세지 전달
			pw.println("메시지를 발송한다. 오버!");
			pw.flush();
			
			// 응답 출력
			System.out.println(br.readLine());
		} catch (IOException e) {
		    System.out.println(e.getMessage());
		} finally {
		    // 소켓 닫기 (연결 끊기)
		    try {
		    	if(socket != null) { socket.close(); }
		        if(br != null) { br.close(); }
		        if(pw != null) { pw.close(); }
		    } catch (IOException e) {
		        System.out.println(e.getMessage());
		    }
		}
	}
}

 

 

 

자바 IO 패키지 예제 - git

자바 스레드 예제 - git