DevOps

[소켓]C / C ++의 소켓 프로그래밍

IT오이시이 2020. 12. 8. 23:19
728x90

[소켓]C / C ++의 소켓 프로그래밍

시스템 프로그래밍을 배우면서 c/c++ 기초로 소켓 프로그램을 이용한  많이 이용했습니다. 네트웍 통신과 데이터를 입력하고 저장하는 가장 기본적인 포인트라든지 네트웍으로 주고 받는 변수에 대한 정의들을 하는 것을 볼수가 있습니다.

소켓이라는 개념으로 TCP/IP 네트웍의 가장 기본적인 기술 매커니즘은 시스템 개발에서 가장 기본적인 내용입니다.

 

소켓 프로그래밍이란 무엇입니까?

Socket 이라는 의미에는 전원 소켓 처럼 쓰고 싶을때 연결해서 사용한다는 의미가 있습니다. 서버와 클라이언트간 필요할때 정의해서 TCP/IP로 통신하는 방식을 소켓프로그램 이라고 하며 소켓프로그램의 구성은 서버와 클라이언트로 나누어 집니다.

소켓프로그램의 작동방식

1) 소켓프로그램 서버 소켓 Port 를 열어두고 Client가 오기 까지 대기(Listen) 합니다.
2) 소켓 클라이언트가 서버에 연결 요청을 하면 3) 서버는 Accept 를 하고 클라이언트와 서버간의 연결되는 과정을 일반적인 소켓 프로그램 방식이 됩니다.
4) 연결이되면 클라이언트에서 서버로 Send로 요청 전문을 보내면
5) 서버는 Request에 대한 Response를 보냅니다.
6) 클라이언트는 서버에서 보내온 전문을 읽는 과정(Recv) 을 반복하게 됩니다.

소켓과 프로토콜

소켓은 HTTP와 같이 서버와 클라이언트가 통신하는 방식입니다. HTTP는 포트번호 (80 , 443 )와 전문 규약이 표준화 된 반면 소켓은 포트번호 부터 통신 내용을 정의해서 사용해야 합니다. 일종의 커스텀 전문 통신 방식 이라고 할까요!
소켓 프로그래밍은 네트워크의 두 노드를 연결하여 서로 통신하는 방법입니다.

소켓프로그래밍

소켓 서버(노드)는 소켓 클라이언트의 요청을 받아 응답 하는것으로 IP 또는 도메인주소와 포트(Port)로 소켓을 생성하고 수신을 위해 리슨(Listen)하여 을답을 밭아 처리합니다

소켓클라이언트는 서버의 IP 또는 도메인과 포트로 요청을 보내고 소켓이 연결되면 요청을 보내고 을답을 받도록 구성하면됩니다.

기초적인 소켓 프로그래밍은 아래 그림과 같이 간단합니다. 그러나 일반적인 프로그램은 대량의 동시 처리를 위해 멀티 쓰레드나 큐잉등 다양한 방법들이 있습니다. 기초부터 이해하면 어려운 부분도 쉽게 이해가 될것입니다.
우선 간단한 소켓서버 부터 확인 바랍니다.

1. 소켓 서버 생성 하기

1) 소켓 생성 (socket)

int sockfd = socket(domain, type, protocol)
sockfd : 소켓 설명자, 파일과 같이 취급하는 핸들러
도메인 (domain) : 정수, 통신 도메인 예, AF_INET (IPv4 프로토콜), AF_INET6 (IPv6 프로토콜)
유형(Type) : 통신 유형 (TCP, UDP 등을 선언)
- SOCK_STREAM : TCP (신뢰성, 연결 지향)
- SOCK_DGRAM : UDP ( unreliable, connectionless)
프로토콜(protocol) : 인터넷 프로토콜 (IP)의 프로토콜 값으로 0입니다. 이는 패킷의 IP 헤더의 프로토콜 필드에 나타나는 것과 동일한 번호입니다.

2) 소켓 옵션 (Setsockopt)

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

이것은 파일 설명자 sockfd가 참조하는 소켓에 대한 옵션을 조작합니다. 선택 사항이지만 주소와 포트를 재사용하는 데 도움이됩니다. "이미 사용중인 주소"와 같은 오류를 방지합니다.

3) 바인딩 (bind)

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

소켓 생성 후 bind 함수는 addr (custom data structure)에 지정된 주소 및 포트 번호에 소켓을 바인딩합니다. 예제 코드에서는 서버를 localhost에 바인딩하므로 INADDR_ANY를 사용하여 IP 주소를 지정합니다.

4) 연결대기(listen)

int listen(int sockfd, int backlog);

서버 소켓을 수동 모드로 설정하여 클라이언트가 서버에 접근하여 연결하기를 기다립니다. 백로그는 sockfd에 대해 보류중인 연결 대기열이 증가 할 수있는 최대 길이를 정의합니다. 큐가 가득 찼을 때 연결 요청이 도착하면 클라이언트가 ECONNREFUSED 표시와 함께 오류를 수신 할 수 있습니다.

5) 수신 동의(Accept)

int new_socket = accept (int sockfd, struct sockaddr * addr, socklen_t * addrlen);

청취 소켓 sockfd에 대해 보류중인 연결 큐에서 첫 번째 연결 요청을 추출하고 새 연결된 소켓을 만들고 해당 소켓을 참조하는 새 파일 설명자를 반환합니다. 이 시점에서 클라이언트와 서버간에 연결이 설정되고 데이터를 전송할 준비가 됩니다.

 

2. 소켓 클라이언트 만들기

 

1) 소켓 서버 연결 : 서버의 소켓 생성과 동일

int connect (int sockfd, const struct sockaddr * addr, socklen_t addrlen);

connect() 시스템 호출은 파일 설명자 sockfd가 참조하는 소켓을 addr에 의해 지정된 주소에 연결합니다. 서버의 주소와 포트는 addr에 지정됩니다.

 

3. 소켓 프로그램 샘플 소스

(서버) server.c

// Server side C/C++ program to demonstrate Socket programming
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[1024] = {0};
char *hello = "Hello from server";

// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
    perror("socket failed");
    exit(EXIT_FAILURE);
}

// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
{
    perror("setsockopt");
    exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );

// Forcefully attaching socket to the port 8080
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
{
    perror("bind failed");
    exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
    perror("listen");
    exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
    perror("accept");
    exit(EXIT_FAILURE);
}

valread = read( new_socket , buffer, 1024);
printf("%s\n",buffer );

send(new_socket , hello , strlen(hello) , 0 );
printf("Hello message sent\n");

return 0;
}

 

(클라이언트) client.c

// Client side C/C++ program to demonstrate Socket programming
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define PORT 8080

int main(int argc, char const *argv[])
{
int sock = 0, valread;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[1024] = {0};

if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
    printf("\n Socket creation error \n");
    return -1;
}

serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);

// Convert IPv4 and IPv6 addresses from text to binary form
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
{
    printf("\nInvalid address/ Address not supported \n");
    return -1;
}

if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
    printf("\nConnection Failed \n");
    return -1;
}

send(sock , hello , strlen(hello) , 0 );

printf("Hello message sent\n");
valread = read( sock , buffer, 1024);
printf("%s\n",buffer );
return 0;
}

 

4. 컴파일 하고 실행하기

gcc client.c -o client
gcc server.c -o server

./server &
./client

* server는 백그라운드로 실행을 하고 client를 실행하여 다른 서버에서 작동되는 듯이 동시에 실행이 가능하다.

[참고] 원문 www.geeksforgeeks.org/socket-programming-cc/

 

Socket Programming in C/C++ - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

728x90
반응형