소켓 프로그래밍

ParkJeongBin·2023년 12월 3일

network

목록 보기
3/6

여러 소켓 프로그래밍 예제를 살펴보며 어떻게 코딩되는지 알아보겠습니다.


유닉스 도메인 소켓 예제

유닉스 도메인 소켓은 같은 시스템에서 통신이 일어나므로 TCP/IP 프로토콜을 직접 이용할 필요가 없다. 따라서 유닉스 도메인 소켓에서 사용하는 소켓 주소 구조체의 항목도 IP 주소가 아닌 경로명을 지정하도록 되어 있다.

유닉스 도메인 소켓 (서버)

#include <sys/socket.h>		// 소켓 프로그래밍을 위한 기본적인 함수나 상수를 정의
#include <sys/un.h>			// 로컬 UNIX 도메인 소켓 프로그래밍을 위한 함수나 상수를 정의
#include <unistd.h>			// 유닉스와 리눅스에서 사용하는 다양한 함수 호출들을 정의
#include <stdlib.h>			// C 표준 라이브러리에서 제공하는 일반적인 유틸리티 함수들을 정의
#include <stdio.h>			// C 표준 라이브러리에서 제공하는 입출력 관련 함수들을 정의
#include <string.h>			//C 표준 라이브러리에서 제공하는 문자열 관련 함수들을 정의

#define SOCKET_NAME "hbsocket" // 소켓의 이름

int main() {
	char buf[256];
    struct sockaar_un ser, cli;
    int sd, nsd, len, clen;
    
    if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {		// UNIX 도메인 소켓 | TCP 소켓 생성
    	perror("socket");
        exit(1);
    }

	memset((char *)&ser, 0, sizeof(stuct sockaddr_un));		// 소켓 주소 구조체를 초기화
    set.sun_family = AF_UNIX;								// 소켓 주소 구조체에서 소켓 패밀리를 AF_UNIX로 지정
    strcpy(ser.sun_path, SOCKET_NAME);						// 소켓 경로명을 지정
    len = sizeof(ser.sun_family) + strlen(ser.sun_path);
    
    if (bind(sd, (struct sockaddr *)&ser, len)) {			// bind() 함수로 소켓 기술자를 소켓 주소 구조체와 연결해 이름을 등록
    	perror("bind");
        exit(1);
    }
    
    if (listen(sd, 5) < 0 {									// listen() 함수를 호출하여 통신할 준비가 되었음을 나타냄
    	perror("listen");
        exit(1);
    }
    
    printf("Waiting ... \n");
    
    if ((nsd = accept(sd, (struct sockaddr *)&cli, &clen)) == -1) { 
    	perror("accept");
        exit(1);
    }														// accept() 함수를 호출하여 클라이언트의 접속 요청을 수락하고 새로운 소켓 기술자를 생성해 nsd에 저장. 클라리언트와는 nsd를 통해 통신을 하게 된다.
   
    if (recv(nsd, buf, sizeof(buf), 0) == -1) {				// 클라이언트가 보낸 메시지를 recv() 함수로 받아서 출력
    	perror("recv");
        exit(1);
    }
    
	printf("Received Message: %s\n", buf);
    close(nsd);
    close(sd);												// 출력을 완료했으므로 소켓을 닫는다.
}
   

유닉스 도메인 소켓 (클라이언트)

#include <sys/socket.h>		// 소켓 프로그래밍을 위한 기본적인 함수나 상수를 정의
#include <sys/un.h>			// 로컬 UNIX 도메인 소켓 프로그래밍을 위한 함수나 상수를 정의
#include <unistd.h>			// 유닉스와 리눅스에서 사용하는 다양한 함수 호출들을 정의
#include <stdlib.h>			// C 표준 라이브러리에서 제공하는 일반적인 유틸리티 함수들을 정의
#include <stdio.h>			// C 표준 라이브러리에서 제공하는 입출력 관련 함수들을 정의
#include <string.h>			//C 표준 라이브러리에서 제공하는 문자열 관련 함수들을 정의

#define SOCKET_NAME "hbsocket"	// 소켓의 이름

int main() {
	int sd, len;
    char buf[256];
    struct sockaddr_un ser;
    
    if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {		// UNIX 도메인 소켓 | TCP 소켓 생성
    	perror("socket");
        exit(1);
    }
    
    memset((char *)&ser, '\0', sizeof(ser)); 				// 소켓 구조체 초기화
    ser.sun_family = AF_UNIX;								// 소켓 패밀리를 AF_UNIX로 지정함
    strcpy(ser.sun_path, SOCKET_NAME);
    len = sizeof(ser.sun_family) + strlen(ser.sun_path);
    
    if (connect(sd, (struct sockaddr *)&ser, len) < 0) {	// 소켓 주소 구조체에 지정한 서버로 connect() 함수를 사용해 연결 요청
    	perror("bind");
        exit(1);
    }
    
    strcpy(buf, "Unix Domain Socket Test Message");			// 서버에 전송할 메시지 지정
    if (send(sd, buf, sizeof(buf), 0) == -1) {				// 서버로 메시지 전송
    	perror("send");
        exit(1);
    }
    close(sd);
}
   

이 코드를 실행하여 클라이언트가 보낸 Unix Domain Socket Test Message를 서버에서 받아 출력했음을 알 수 있다. (서버를 먼저 실행 시켜야 함)

한 번 실행하고 다시 실행하면 bind: Address already in use 라는 오류가 뜨니

unlink(SOCKET_NAME);

을 실행하여 파일을 지우도록 해야 함



인터넷 소켓 예제

인터넷 소켓은 서로 다른 시스템 사이에 통신하므로 TCP/IP 프로토콜을 직접 이용한다.
따라서 소켓 주소 구조체의 항목에 IP 주소와 포트 번호를 지정해야 한다.

#include <sys/socket.h>		// 소켓 프로그래밍을 위한 기본적인 함수나 상수를 정의
#include <sys/un.h>			// UNIX 도메인 소켓 프로그래밍을 위한 함수나 상수를 정의
#include <unistd.h>			// 유닉스, 리눅스에서 사용하는 다양한 함수 호출들을 정의
#include <arpa/inet.h>		// 네트워크 프로그래밍에 사용되는 함수들과 상수들을 정의
#include <stdlib.h>			
#include <string.h>
#include <stdio.h>

#define PORTNUM 9000 // 포트 번호 지정

int main() {
	char buf[256];
    struct sockaddr_in sin, cli;
    int sd, ns, clientlen = sizeof(cli);
    
    if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {		// 인터넷 | TCP 소켓 생성
    	perror("socket");
        exit(1);
    }
    
    memset((char *)&sin, '\0', sizeof(sin));				// 소켓 구조체 초기화
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORTNUM);
    sin.sin_addr.s_addr = inet_addr("192.168.147.129"); 	// 서버의 IP 주소 (192.168.147.129)를 지정하고 포트 번호는 9000으로 지정해 소켓 주소 구조체를 생성
    
    if (bind(sd, (struct sockaddr *)&sin, sizeof(sin))) {	// bind() 함수로 소켓 이름을 정함
    	perror("bind");
        exit(1);
    }
    
    if (listen(sd, 5)) {									// listen() 함수를 호출해 접속 요청을 받을 준비를 마쳤음을 알림
    	perror("listen");
        exit(1);
    }
    
    if ((ns = accept(sd, (struct sockaddr *)&cli, &clientlen)) == -1 ) {
    	perror("accept");
        exit(1);
    }														// accept() 함수로 클라이언트 요청 수락
    
    sprintf("buf, "Your IP address is %s", inet_ntoa(cli.sin_addr)); // 접속한 클라이언트의 IP 주소를 읽어 메시지를 작성
    if (send(ns, buf, strlen(buf) + 1, 0) == -1) {			// send() 함수로 메시지를 전송
    	perror("send");
        exit(1);
    }
    
    close(ns);
    close(sd);												// 사용을 마친 소켓을 모두 닫는다.
}
         

인터넷 소켓 (클라이언트)

#include <sys/un.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define PORTNUM 9000 // 포트 번호 지정

int main() {
	int sd;
    char buf[256];
    struct sockaddr_in sin;
    
    if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {	// 인터넷 | TCP 소켓 생성
    	perror("socket");
        exit(1);
    }
    
    memset((char *)&sin, '\0', sizeof(sin));				// 구조체 초기화 
    sin.sin_family = AF_INET;
    sin.sin_port = htons(PORTNUM);
    sin.sin_addr.s_addr = inet_addr("192.168.147.129");		// IP와 포트 번호 지정
    
    if (connect(sd, (struct sockaddr *)&sin, sizeof(sin))) { // connect() 함수를 사용해 서버에 연결 요청
    	perror("connect");
        exit(1);
    }
    
    if (recv(sd, buf, sizeof(buf), 0) == -1) {				// 연결되면 서버에서 오는 메시지를 받음
    	perror("recv");
        exit(1);
    }
    
    close(sd);												// 사용을 마친 소켓을 닫음
    printf("From Server : %s\n", buf);						// 서버에서 온 메시지 출력
}

서버에서 보낸 메시지를 클라이언트에서 출력함을 할 수 있음 ( 서버 프로그램을 먼저 실행시켜야 한다. )

한줄 한줄 코딩하며 모르는 부분을 검색하고 주석을 달며 해석하였습니다. 확실히 여러 번 작성해 보는 것이 실력이 빠르게 향상되는 것 같습니다. 다음 번에는 TCP 기반 프로그래밍을 해보겠습니다.

profile
My dream is White-Hat

0개의 댓글