Sunday, March 27, 2016

Node.js 서버 프로그래밍 배우기 가이드 (로드맵)


Node.js를 활용한 서버 프로그래밍을 위해 알아야 하는 것들.
        1. (기본) Javascript 언어
              -> 서버와 클라이언트에서 모두 사용된다.
              -> 참조 사이트
                      튜토리얼  https://developer.mozilla.org/ko/docs/Web/JavaScript/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0
                      강의
                       http://math88.com.ne.kr/computer/js/JSmanual/
                      영어에 익숙하다면 W3CSchool이 체계적으로 정리되어 있다.
                       http://www.w3schools.com/js/

        2. (기본) Node.js 이해하기
                      Javascript를 서버에서 사용하기 위한 프레임워크라고 이해하면 된다.
                     (즉 클라이언트단에서만 사용되던 Javascript를 서버에서도 활용할 수 있도록 함)
                 
                      설치하기
                       https://nodejs.org/ko/

                      튜토리얼
                      http://www.nodebeginner.org/index-kr.html
                      http://pyrasis.com/nodejs/nodejs-HOWTO

         3. (기본) 자바 스크립트에서include/import 구현 규약
                     Java나 C++처럼 미리 작성된 코드를 include/import하여 사용할 수 있도록 함.
                     코드의 재활용(Reuse)을 가능하게 해준다.
                     대표적으로 CommonJS 규약과 AMD 규약이 있다.
                      -> node.js에서는 CommonJS 규약을 사용함.

                      참조: http://programmingsummaries.tistory.com/321

         4. (기본) npm -> Node.js용 모듈 관리자.
                     Node.js의 강점은 엄청나게 많은 모듈들이 작성되어 공개되어 있다는 것이다.
                     따라서 좋은 모듈을 찾아 사용함으로써 개발 시간을 크게 단축시킬 수 있다.
                   
                     npm 공식 홈페이지
                          https://www.npmjs.com/

                     npm 튜토리얼
                          위 node.js 기본 튜토리얼에 npm 사용법 설명이 포함되어 있습니다.
                          (별도 참조) https://blog.outsider.ne.kr/638

         5. (기본) Express -> Node.js용 웹 서버 모듈.
                     Node.js가 기본적으로 웹 서버를 제공하지만, middleware 방식으로 좀 더
                     사용하기 편하게 만든 웹 서버 프레임워크. 거의 대부분의 웹 서버가 이 모듈을
                     사용한다고 보면 됩니다.
   
                     참조
                            http://expressjs.com/ko/

          6. (응용) Passport -> Node.js용 범용 사용자 인증 모듈
                     이메일/암호 방식을 이용한 기본 사용자 인증, 페이스북이나 구글 계정을
                     연동한 인증등 거의 대부분의 인증 방식을 제공합니다.

                      참조
                            (한글) http://bcho.tistory.com/920
                            (영어) http://passportjs.org/

           7. (응용) Socket.io -> 서버/클라이언트간 실시간 정보 교환
                     서버와 클라이언트가 실시간으로 정보를 교환할 수 있도록 해준다. 예를 들면,
                     웹 기반 채팅 프로그램 같은 것을 쉽게 작성할 수 있다.

                      참조
                           (한글) http://bcho.tistory.com/896
                           (영어) http://socket.io/get-started/chat/

           8. (응용) MongoDB -> 서버용 NoSQL 데이터베이스
                      json과 유사한 형식으로 레코드를 저장하는 NoSQL 데이터베이스이다.
                      꽤 많이 사용되고 있다. node.js뿐만 아니라 다양한 개발 환경을 지원한다.
                   
                      참조
                           (한글) http://bcho.tistory.com/742
                                       http://daddycat.blogspot.kr/2013/02/mongodb-1.html
                           (영어) https://www.mongodb.org/

            9. (응용) mongoose
                     위 8. 항목의 mongodb를 좀 더 쉽게 사용하기 위한 node.js용 모듈
                     
                      참조.
                            (한글) http://devdoc.tistory.com/5
                                       http://bcho.tistory.com/890
                            (영어) http://mongoosejs.com/docs/

           10. (응용) Promise 프로토콜
                       node.js는 기본적으로 콜백(callback)을 기반으로 합니다.  단일 제어 흐름을
                       기본으로 하고, 시간이 걸리는 작업들은 백그라운드에서 처리후 콜백(callback)을
                       호출해주는 방식이죠. 여기에서 한가지 문제가 발생하는데, 좀 복잡한 작업을
                       처리하려고 하면 callback 함수들의 중첩이 감당할 수 없을 정도로 늘어납니다.

                       CallFunctionA(a, b, function(c, d) {
                                   ....
                                   CallFunctionB(e, f, function(g, h) {
                                               ....
                                              CallFunctionC(i, j, function(k, l) {
                                                         .....
                                              });
                                               ....
                                    });
                                    ....
                         });

                        이 경우, 코드를 읽는 것도 힘들어지고, 중복된 코드들이 다량으로 발생하는
                        문제점이 있습니다. (예를 들면, 예외 처리 코드들).

                        이 문제를 해결하기 위해 나온 것이 Promise라는 개념입니다.

                        참조
                               (한글) http://programmingsummaries.tistory.com/325
                                           http://www.html5rocks.com/ko/tutorials/es6/promises/
                               (영어) https://www.promisejs.org/

Friday, June 21, 2013

NSNetService initWithDomain example in Objective C (iOS).


NSNetService initWithDomain

Returns the receiver, initialized as a network service of a given type and sets the initial host information.

- (id)initWithDomain:(NSString *)domain type:(NSString *)type name:(NSString *)name

Parameters of [NSNetService initWithDomain]
domain
The domain for the service. For the local domain, use @"local." not @"".
type
The network service type.
type must contain both the service type and transport layer information. To ensure that the mDNS responder searches for services, as opposed to hosts, prefix both the service name and transport layer name with an underscore character (“_”). For example, to search for an HTTP service on TCP, you would use the type string "_http._tcp.". Note that the period character at the end of the string, which indicates that the domain name is an absolute name, is required.
name
The name of the service to resolve.

Return Value of [NSNetService initWithDomain]
The receiver, initialized as a network service named name of type type in the domain domain.

Discussion of [NSNetService initWithDomain]
This method is the appropriate initializer to use to resolve a service—to publish a service, use initWithDomain:type:name:port:.

If you know the values for domain, type, and name of the service you wish to connect to, you can create an NSNetService object using this initializer and call resolveWithTimeout: on the result.

You cannot use this initializer to publish a service. This initializer passes an invalid port number to the designated initializer, which prevents the service from being registered. Calling publish on an NSNetService object initialized with this method generates a call to your delegate’s netService:didNotPublish: method with an NSNetServicesBadArgumentError error.

NSNetService initWithDomain example.
I'm not sure, but it looks like the request is not actually scheduled in a run loop for some reason. Maybe try something like this to schedule it?

NSNetService *service = [[[NSNetService alloc] initWithDomain:@"local." type:@"_daap._tcp." name:@"My_Mac"] autorelease];
[service setDelegate:self];
[service scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:@"PrivateMyMacServiceMode"];
[service resolveWithTimeout:8.0];
Stupid question, but are you explicitly implementing NSServiceDelegate protocol or just have the methods?

Example of [NSNetService initWithDomain].
conclusion: publish by first obtaining a port and then use something like:

self.netService = [[NSNetService alloc] initWithDomain:@"local" type:@"_xxx._tcp." name:serviceName port:(int) self.port];
open streams with
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
open streams with (you open a connection here)
EchoConnection * connection = [[EchoConnection alloc] initWithInputStream:( NSInputStream *)readStream outputStream:( NSOutputStream *)writeStream];
        [self.connections addObject:connection];
then browse for services and add them

then open streams on the desired services from the one you browsed with

[nService qNetworkAdditions_getInputStream:&istream outputStream:&ostream];
(and open them with [istream open] and [ostream open])

NSNetService initWithDomain example.
#import "ServerController.h"

@interface ServerController ()

-(void)startService;
-(void)stopService;

@end

@implementation ServerController

-(void)awakeFromNib {   
    [self startService];
}

-(void)startService {
    netService = [[NSNetService alloc] initWithDomain:@"" type:@"_cocoaforsci._tcp."
        name:@"" port:7865];
    netService.delegate = self;
    [netService publish];
}

-(void)stopService {
    [netService stop];
    [netService release];
    netService = nil;
}

-(void)dealloc {
    [self stopService];
    [super dealloc];
}

#pragma mark Net Service Delegate Methods
-(void)netService:(NSNetService *)aNetService didNotPublish:(NSDictionary *)dict {
    NSLog(@"Failed to publish: %@", dict);
}

@end

End of NSNetService initWithDomain example article.

NSNetService initWithDomain type name example in Objective C (iOS).


NSNetService initWithDomain type name

Returns the receiver, initialized as a network service of a given type and sets the initial host information.

- (id)initWithDomain:(NSString *)domain type:(NSString *)type name:(NSString *)name

Parameters of [NSNetService initWithDomain type name]
domain
The domain for the service. For the local domain, use @"local." not @"".
type
The network service type.
type must contain both the service type and transport layer information. To ensure that the mDNS responder searches for services, as opposed to hosts, prefix both the service name and transport layer name with an underscore character (“_”). For example, to search for an HTTP service on TCP, you would use the type string "_http._tcp.". Note that the period character at the end of the string, which indicates that the domain name is an absolute name, is required.
name
The name of the service to resolve.

Return Value of [NSNetService initWithDomain type name]
The receiver, initialized as a network service named name of type type in the domain domain.

Discussion of [NSNetService initWithDomain type name]
This method is the appropriate initializer to use to resolve a service—to publish a service, use initWithDomain:type:name:port:.

If you know the values for domain, type, and name of the service you wish to connect to, you can create an NSNetService object using this initializer and call resolveWithTimeout: on the result.

You cannot use this initializer to publish a service. This initializer passes an invalid port number to the designated initializer, which prevents the service from being registered. Calling publish on an NSNetService object initialized with this method generates a call to your delegate’s netService:didNotPublish: method with an NSNetServicesBadArgumentError error.

NSNetService initWithDomain type name example.
I'm not sure, but it looks like the request is not actually scheduled in a run loop for some reason. Maybe try something like this to schedule it?

NSNetService *service = [[[NSNetService alloc] initWithDomain:@"local." type:@"_daap._tcp." name:@"My_Mac"] autorelease];
[service setDelegate:self];
[service scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:@"PrivateMyMacServiceMode"];
[service resolveWithTimeout:8.0];
Stupid question, but are you explicitly implementing NSServiceDelegate protocol or just have the methods?

Example of [NSNetService initWithDomain type name].
conclusion: publish by first obtaining a port and then use something like:

self.netService = [[NSNetService alloc] initWithDomain:@"local" type:@"_xxx._tcp." name:serviceName port:(int) self.port];
open streams with
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
open streams with (you open a connection here)
EchoConnection * connection = [[EchoConnection alloc] initWithInputStream:( NSInputStream *)readStream outputStream:( NSOutputStream *)writeStream];
        [self.connections addObject:connection];
then browse for services and add them

then open streams on the desired services from the one you browsed with

[nService qNetworkAdditions_getInputStream:&istream outputStream:&ostream];
(and open them with [istream open] and [ostream open])

NSNetService initWithDomain type name example.
#import "ServerController.h"

@interface ServerController ()

-(void)startService;
-(void)stopService;

@end

@implementation ServerController

-(void)awakeFromNib {   
    [self startService];
}

-(void)startService {
    netService = [[NSNetService alloc] initWithDomain:@"" type:@"_cocoaforsci._tcp."
        name:@"" port:7865];
    netService.delegate = self;
    [netService publish];
}

-(void)stopService {
    [netService stop];
    [netService release];
    netService = nil;
}

-(void)dealloc {
    [self stopService];
    [super dealloc];
}

#pragma mark Net Service Delegate Methods
-(void)netService:(NSNetService *)aNetService didNotPublish:(NSDictionary *)dict {
    NSLog(@"Failed to publish: %@", dict);
}

@end

End of NSNetService initWithDomain type name example article.

NSNetService hostName example in Objective C (iOS).


NSNetService hostName

Returns the host name of the computer providing the service.

- (NSString *)hostName

Return Value of [NSNetService hostName]
The host name of the computer providing the service. Returns nil if a successful resolve has not occurred.

NSNetService hostName example.
I believe that "hows-testing" is the host name of the computer, not the name of the service. Instead, you want to look at the hostName attribute:

- (void)netServiceDidPublish:(NSNetService *)ns
{
   NSLog(@"Bonjour Service Published: http://%@", [ns hostName]);
}
You should probably also examine the port attribute, and include it in the URL, if the port is something other than the default for that protocol (80 for HTTP)

End of NSNetService hostName example article.

NSNetService getInputStream outputStream example in Objective C (iOS).


NSNetService getInputStream outputStream

Retrieves by reference the input and output streams for the receiver and returns a Boolean value that indicates whether they were retrieved successfully.

- (BOOL)getInputStream:(NSInputStream **)inputStream outputStream:(NSOutputStream **)outputStream

Parameters
inputStream
Upon return, the input stream for the receiver.
outputStream
Upon return, the output stream for the receiver.

Return Value
YES if the streams are created successfully, otherwise NO.

Discussion of [NSNetService getInputStream outputStream]
After this method is called, no delegate callbacks are called by the receiver.

NSNetService getInputStream outputStream example.
Don't believe everything apple claims,

There are a lot of problems with setting up streams with the NSNetworkService.

It will work if you do the following:

First obtain a port to publish the network on. DO NOT PICK A PORT YOURSELF.

Then you can use that port for publishing the network.

Get the client streams with:

[nService qNetworkAdditions_getInputStream:&istream outputStream:&ostream];
The error a lot of programmers make is publishing a network by picking a port themselves and then opening the streams with

[nService qNetworkAdditions_getInputStream:&istream outputStream:&ostream];
The streams will not open....

conclusion: publish by first obtaining a port and then use something like:

self.netService = [[NSNetService alloc] initWithDomain:@"local" type:@"_xxx._tcp." name:serviceName port:(int) self.port];
open streams with
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
open streams with (you open a connection here)
EchoConnection * connection = [[EchoConnection alloc] initWithInputStream:( NSInputStream *)readStream outputStream:( NSOutputStream *)writeStream];
        [self.connections addObject:connection];
then browse for services and add them

then open streams on the desired services from the one you browsed with

[nService qNetworkAdditions_getInputStream:&istream outputStream:&ostream];
(and open them with [istream open] and [ostream open])

Example of [NSNetService getInputStream outputStream].
I have just changed my method as below

NSInputStream  *tempInput  = nil;
NSOutputStream *tempOutput = nil;

// note the following method returns _inStream and _outStream with a retain count that the caller must eventually release
if (![netService getInputStream:&tempInput outputStream:&tempOutput]) {

    NSLog(@"error in get input and output streams");
    return;
}
_inStream  = tempInput;
_outStream = tempOutput;

NSNetService getInputStream outputStream example.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 if (inputStream && outputStream) [self closeStreams];
   
 if (indexPath.row) {
NSNetService * selectedService = [serviceList objectAtIndex:indexPath.row];
if ([selectedService getInputStream:&inputStream outputStream:&outputStream]) [self openStreams];
serverLabel.text = [[serviceList objectAtIndex:indexPath.row] name];
}
 }

End of NSNetService getInputStream outputStream example article.

NSNetService getInputStream example in Objective C (iOS).


NSNetService getInputStream

Retrieves by reference the input and output streams for the receiver and returns a Boolean value that indicates whether they were retrieved successfully.

- (BOOL)getInputStream:(NSInputStream **)inputStream outputStream:(NSOutputStream **)outputStream

Parameters
inputStream
Upon return, the input stream for the receiver.
outputStream
Upon return, the output stream for the receiver.

Return Value
YES if the streams are created successfully, otherwise NO.

Discussion of [NSNetService getInputStream]
After this method is called, no delegate callbacks are called by the receiver.

NSNetService getInputStream example.
Don't believe everything apple claims,

There are a lot of problems with setting up streams with the NSNetworkService.

It will work if you do the following:

First obtain a port to publish the network on. DO NOT PICK A PORT YOURSELF.

Then you can use that port for publishing the network.

Get the client streams with:

[nService qNetworkAdditions_getInputStream:&istream outputStream:&ostream];
The error a lot of programmers make is publishing a network by picking a port themselves and then opening the streams with

[nService qNetworkAdditions_getInputStream:&istream outputStream:&ostream];
The streams will not open....

conclusion: publish by first obtaining a port and then use something like:

self.netService = [[NSNetService alloc] initWithDomain:@"local" type:@"_xxx._tcp." name:serviceName port:(int) self.port];
open streams with
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
open streams with (you open a connection here)
EchoConnection * connection = [[EchoConnection alloc] initWithInputStream:( NSInputStream *)readStream outputStream:( NSOutputStream *)writeStream];
        [self.connections addObject:connection];
then browse for services and add them

then open streams on the desired services from the one you browsed with

[nService qNetworkAdditions_getInputStream:&istream outputStream:&ostream];
(and open them with [istream open] and [ostream open])

Example of [NSNetService getInputStream].
I have just changed my method as below

NSInputStream  *tempInput  = nil;
NSOutputStream *tempOutput = nil;

// note the following method returns _inStream and _outStream with a retain count that the caller must eventually release
if (![netService getInputStream:&tempInput outputStream:&tempOutput]) {

    NSLog(@"error in get input and output streams");
    return;
}
_inStream  = tempInput;
_outStream = tempOutput;

NSNetService getInputStream example.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 if (inputStream && outputStream) [self closeStreams];
   
 if (indexPath.row) {
NSNetService * selectedService = [serviceList objectAtIndex:indexPath.row];
if ([selectedService getInputStream:&inputStream outputStream:&outputStream]) [self openStreams];
serverLabel.text = [[serviceList objectAtIndex:indexPath.row] name];
}
 }

End of NSNetService getInputStream example article.

NSNetService addresses example in Objective C (iOS).


NSNetService addresses

Returns an array containing NSData objects, each of which contains a socket address for the service.

- (NSArray *)addresses

Return Value
An array containing NSData objects, each of which contains a socket address for the service. Each NSData object in the returned array contains an appropriate sockaddr structure that you can use to connect to the socket. The exact type of this structure depends on the service to which you are connecting. If no addresses were resolved for the service, the returned array contains zero elements.

Discussion of [NSNetService addresses]
It is possible for a single service to resolve to more than one address or not resolve to any addresses. A service might resolve to multiple addresses if the computer publishing the service is currently multihoming.

NSNetService addresses example.
for (NSData* data in [server addresses]) {

    char addressBuffer[100];

    struct sockaddr_in* socketAddress = (struct sockaddr_in*) [data bytes];

    int sockFamily = socketAddress->sin_family;

    if (sockFamily == AF_INET || sockFamily == AF_INET6) {

        const char* addressStr = inet_ntop(sockFamily,
                            &(socketAddress->sin_addr), addressBuffer,
                            sizeof(addressBuffer));

        int port = ntohs(socketAddress->sin_port);

        if (addressStr && port)
            NSLog(@"Found service at %s:%d", addressStr, port);

    }

}

Example of [NSNetService addresses].
for (NSData *address in [service addresses]) {
struct sockaddr_in *socketAddress = (struct sockaddr_in *) [address bytes];
            NSLog(@\"Service name: %@ , ip: %s , port %i\", [service name], inet_ntoa(socketAddress->sin_addr), [service port]);
        }

NSNetService addresses example.
@implementation NSNetService (ipv4)

- (NSArray*)ipv4addresses
{
        NSMutableArray * ipv4addresses = [NSMutableArray array];
        NSArray *addresses = [self addresses];
        int aCount = [addresses count];
        char addr[256];
       
        for (int i = 0; i < aCount; i++) {
                struct sockaddr *socketAddress = (struct sockaddr *)[[addresses 
objectAtIndex:i] bytes];
               
                if (socketAddress && socketAddress->sa_family == AF_INET) {
                        if (inet_ntop(AF_INET, &((struct sockaddr_in *)socketAddress)-
 >sin_addr, addr, sizeof(addr))) {
                               
                                uint16_t port = ntohs(((struct sockaddr_in *)socketAddress)-
 >sin_port);
                               
                                [ipv4addresses addObject:[NSString stringWithFormat:@"%s:%d", 
addr, port]];
                        }
                }
        }
       
        return ipv4addresses;
}

@end


End of NSNetService addresses example article.