본문 바로가기
#컴퓨터 과학 [Computer Science]/네트워크 (Network)

[네트워크] UDS (Unix Domain Socket)

by cy_mos 2021. 10. 1.
반응형
카테고리 게시글 작성 날짜 게시글 최근 수정 날짜 작성자
Network 2021.10.01. 05:30 2022.04.06. 23:02 Dev.Yang

 

Unix Domain Socket은 IPC socket (Inter-Process Communication Socket) 이라고도 불리며, TCP (전송 제어 프로토콜, Transmission Control Protocol)의 소켓과 동일한 구조로 데이타를 주고 받을 수 있는 Local File 형식 기반의 소켓입니다. 즉, 동일한 시스템 내에서 파일을 통하여 실행되는 프로세스들 사이의 양방향 데이터 교환을 허용하는 프로세스 간 통신 메커니즘이다.

 

  • Unix Domain Socket은 localhost의 각 Process 통신이 되므로 속도가 매우 빠르며 메모리 사용량이 적다는 장점이 있습니다.
  • TCP (전송 제어 프로토콜, Transmission Control Protocol) 프로토콜을 사용하는 네트워크 작업과 달리 UDS (Unix Domain Socke)는 Routing 작업을 수행하지 않습니다.
  • UDS (Unix Domain Socke) 사용하여 통신 작업을 수행할 경우에는 Local File 파일 단위로 통신 작업이 이루어지기에 파일에 접근권한을 제어하여 간단하게 서버와 클라이언트 단위로 프로세스 통신을 제어할 수 있습니다.

 

/*
 * Copyright (c) 2023 Universal-SystemKit. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
 
#include <sys/un.h>
#include <sys/socket.h>

#define SK_SOCKET_SUCCESS 0
#define SK_SOCKET_FAIL -1
#define SK_SOCKET_EMPTY_PADDING 0
#define SK_CONNECT_REQUEST_QUEUE_MAX 5

#pragma mark - Typedef
typedef void(^SKCompletionHandler)(NSString * message);

@interface SKUnixSocket : NSObject

@property (nonatomic, copy, readwrite) SKCompletionHandler handler;

- (const int) listen: (NSString *) socketPath;

- (const bool) send: (NSString *) socketPath
                   : (NSString *) base64String;

@end

@implementation SKUnixSocket

#pragma mark - Private Instance Method
- (const int) makeUnixSocket {
        
    const int result = socket(AF_UNIX, SOCK_STREAM, 0);
    
    if (result < SK_SOCKET_SUCCESS) {
        NSLog(@"[SKUnixSocket] Failed to create a Unix Domain Socket");
        return SK_SOCKET_FAIL;
    }
    
    NSLog(@"[SKUnixSocket] Successfully created a Unix Domain Socket: %d", result);
    
    return result;
}

- (const struct sockaddr_un) makeUnixSockAddr: (NSString *) path {
    
    // sockaddr_un 구조체를 생성하고 초기화 합니다.
    struct sockaddr_un sockAddr;
    memset(&sockAddr, SK_SOCKET_EMPTY_PADDING, sizeof sockAddr);

    sockAddr.sun_family = AF_UNIX;
    strcpy(sockAddr.sun_path, path.UTF8String);

    return sockAddr;
}

- (void) handleClient: (const int) clientSocket {
    
    char buffer[256];
    memset(&buffer, SK_SOCKET_EMPTY_PADDING, sizeof buffer);
    
    while (true) {
        
        const ssize_t length = read(clientSocket, buffer, 255);
        if (length < 0) {
            //
            close(clientSocket);
            break;
        }
        
        //
        NSString * result = [NSString stringWithUTF8String: buffer];
        self.handler(result);
    }
}

- (void) acceptClients: (const int) serverSocket {
    
    @synchronized (self) {
            
        while (true) {
            
            const int clientSocket = accept(serverSocket, NULL, NULL);
            
            if (clientSocket < SK_SOCKET_SUCCESS) {
                NSLog(@"Error: Accept failed.");
                continue;
            }
            
            const dispatch_queue_t dispatch = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
            
            dispatch_async(dispatch, ^{
                [self handleClient: clientSocket];
            });
        }
    }
}

#pragma mark - Public Instance Method
- (const int) listen: (NSString *) socketPath {
    
    @autoreleasepool {
        
        // Unix Domain Socket 기반의 TCP 소켓을 생성합니다.
        const int serverSocket = [self makeUnixSocket];
        
        // 정상적으로 UDS (Unix Domain Socket) 생성 여부를 확인합니다.
        if (serverSocket == SK_SOCKET_FAIL) { return false; }
        
        // sockaddr_un 생성하고 초기화 합니다.
        const struct sockaddr_un serverAddr = [self makeUnixSockAddr: socketPath];
        
        //
        unlink(socketPath.UTF8String);
        
        //
        const int result = bind(serverSocket, (struct sockaddr *) &serverAddr, sizeof serverAddr);
        if (result < SK_SOCKET_SUCCESS) {
            NSLog(@"Error: Binding failed.");
            return false;
        }
        
        //
        listen(serverSocket, SK_CONNECT_REQUEST_QUEUE_MAX);
        
        //
        [self acceptClients: serverSocket];
        
        // 생성 한 서버 소켓을 종료합니다.
        return close(serverSocket);
    }
}

- (const bool) send: (NSString *) socketPath
                   : (NSString *) message {
    
    @autoreleasepool {
                            
        // Unix Domain Socket 기반의 TCP 소켓을 생성합니다.
        const int clientSocket = [self makeUnixSocket];
        
        // 정상적으로 UDS (Unix Domain Socket) 생성 여부를 확인합니다.
        if (clientSocket == SK_SOCKET_FAIL) { return false; }
        
        const struct sockaddr_un serverAddr = [self makeUnixSockAddr: socketPath];
        
        const int result = connect(clientSocket, (struct sockaddr *) &serverAddr, sizeof serverAddr);
        if (result < SK_SOCKET_SUCCESS) {
            NSLog(@"[SKUnixSocket] Failed to connect to the server");
            return false;
        }

        // 서버에 데이터를 전송합니다.
        const ssize_t length = write(clientSocket, message.UTF8String, message.length);
        NSLog(@"[SKUnixSocket] Data has been transmitted to the server: %zd Bytes", length);
        
        // 생성 한 클라이언트 소켓을 종료합니다.
        close(clientSocket);
        
        return length == message.length;
    }
}

@end

🛠 Unix Domain Socket Type

  • SOCK_STREAM (Compare to TCP): for a stream-oriented socket.
  • SOCK_DGRAM (Compare to UDP): for a datagram-oriented socket that preserves message boundaries (as on most UNIX implementations, UNIX domain datagram sockets are always reliable and don't reorder datagrams).
  • SOCK_SEQPACKET (Compare to SCTP): for a sequenced-packet socket that is connection-oriented, preserves message boundaries, and delivers messages in the order that they were sent.

🛠 Unix Domain Socket 관련 명령어

  • Llsof: 실행 중인 파일과 프로세스의 정보를 출력하는 명령어입니다. 시스템 전체의 UDS (Unix Domain Socket) 목록을 확인하기 위해서는 -U 옵션을 사용합니다.
  • Lls: 디렉터리 목록을 출력하는 명령어입니다. 디렉터리 내부의 파일들에 대한 UDS (Unix Domain Socket) 사용여부를 확인하고자 하는 경우에는 -luh 옵션을 사용합니다. 아래의 예시와 같이 디렉터리나 파일의 권한을 나타내는 부분에 s 문자가 표시 된 파일은 UDS (Unix Domain Socket) 통신을 수행하고 있는 소켓 파일을 나타냅니다.

UDS (Unix Domain Socket) 표시 문구 [출처: https://www.lesstif.com/]


📣 REFERENCE

더보기
반응형

댓글