일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- JanusWebRTCServer
- 깡돼후
- k8s #kubernetes #쿠버네티스
- table not found
- JanusGateway
- preemption #
- 겨울 부산
- 코루틴 컨텍스트
- mp4fpsmod
- 개성국밥
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- 코루틴 빌더
- python
- terminal
- 달인막창
- 티스토리챌린지
- PytestPluginManager
- JanusWebRTC
- Value too long for column
- pytest
- 오블완
- PersistenceContext
- taint
- 자원부족
- tolerated
- VARCHAR (1)
- Spring Batch
- JanusWebRTCGateway
- vfr video
- kotlin
너와 나의 스토리
[실습] 네트워크 프로그래밍과 소켓 - 소켓 인터페이스, 서버 클라이언트 연결 본문
네트워크 프로그래밍
- 소켓 기반 프로그래밍
- 네트워크로 연결되어 있는 서로 다른 두 컴퓨터가 데이터를 주고받을 수 있도록 하는 것
인터넷이라는 거대한 네트워크로 연결되어 있으므로 물리적인 연결은 신경 쓸 필요 없다.
소켓
- 연결된 네트워크 상에서 데이터 송수신에 사용할 수 있는 운영체제가 제공하는 소프트웨어적인 장치
- 네트워크를 통한 두 컴퓨터의 연결을 의미하기도 한다.
- 소켓은 프로그래머에게 데이터 송수신에 대한 물리적, 소프트웨어적(TCP/UDP 프로토콜의) 세세한 내용을 신경 쓰지 않게 한다.
- TCP 소켓은 전화기에 비유될 수 있다.
리눅스에서 실습
저는 Cygwin을 설치해 실습하였습니다.
- 컴파일
gcc helloworld.c -o hello -> helloworld.c 파일을 컴파일해서 hello라는 이름의 실행파일을 만듦
./hello -> 현재 디렉토리에 있는 hello라는 이름의 파일을 실행
- 소켓의 종류
- unix domain socket(AF_UNIX): 같은 호스트에서 프로세스 사이에 통신할 때 사용
- internet socket(AF_INET): 인터넷을 통해 다른 호스트와 통신할 때 사용
- <sys/socket.h>에 정의되어 있는 주소 패밀리명을 사용
- 소켓의 통신 방식 → 소켓 타입
- TCP/IP 프로토콜에서 전송 계층에서 사용하는 프로토코로는 TCP와 UDP가 있다. 소켓을 이용할 때도 하부 프로토콜로 둘 중 무엇을 사용할지 지정해야한다.
- SOCK_STREAM: TCP 프로토콜 사용
- 중간에 데이터가 소멸되지 않는다.
- 전송 순서대로 데이터가 수신된다.
- 데이터의 경계가 존재하지 않는다.
- 소켓 대 소켓의 연결은 반드시 1대 1 구조
- SOCK_DGRAM: UDP 프로토콜 사용
- 전송 순서 상관없이 빠른 속도의 전송을 목적으로 함
- 데이터 손실 및 파손의 우려가 있다.
- 데이터의 경계가 존재한다.
- 한번에 전송할 수 있는 데이터의 크기가 제한된다.
- SOCK_STREAM: TCP 프로토콜 사용
- TCP/IP 프로토콜에서 전송 계층에서 사용하는 프로토코로는 TCP와 UDP가 있다. 소켓을 이용할 때도 하부 프로토콜로 둘 중 무엇을 사용할지 지정해야한다.
TCP에서 "데이터의 경계가 존재하지 않는다."의 의미?
데이터를 송수신하는 소켓은 내부적으로 버퍼(바이트 배열)를 지니고 있다. 그리고 소켓을 통해 전송되는 데이터는 일단 이 배열에 저장된다. 때문에 데이터가 수신되었다고 해서 바로 read 함수를 호출해야 하는 것은 아니다. 이 배열의 용량을 초과하지 않는 한, 데이터가 버퍼에 채워진 후에 한 번의 read 함수 호출을 통해서 데이터 전부를 읽어 들일 수도 있고, (write 함수로 전송된) 데이터 전부를 여러 번의 read 함수 호출을 통해서 읽어 들일 수도 있다. 즉, read 함수의 호출 횟수와 write 함수의 호출 횟수는 연결지향형 소켓의 경우 큰 의미를 갖지 맞한다. -> 데이터의 경계가 존재하지 않음
=> 버퍼가 꽉 차도 데이터가 소멸되지 않는다.
UDP에서 "데이터의 경계가 존재한다."의 의미?
UDP는 패킷 단위로 나눠서 전송한다. 그렇기 때문에 write한 횟수만큼 read하게 된다.
- (전화 받는 -> server) 전화기에 해당하는 소켓을 생성 - socket
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
- domain: 소켓이 사용할 프로토콜 체제(Protocol Family) 정보 전달
- type: 소켓의 데이터 전송방식에 대한 정보 전달 (소켓 타입)
- protocol ->첫 번째, 두 번째 인자로 전달된 정보를 통해서 소켓의 프로토콜이 사실상 결정되기 때문에 세 번째 인자로 0을 전달해도 된다
- 소켓 생성 성공 시 파일 디스크립터, 실패 시 -1 반환
- TCP 소켓을 전화기에 비유
프로토콜 체계(Protocol Family)
- 전화번호 부여 - bind
- 소켓의 주소 할당 및 연결
- 소켓의 주소 정보는 IP와 PORT번호로 구성된다. -> IP, Port 번호 부여
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
- 성공 시 0, 실패 시 -1 반환
- 전화기가 연결 가능하도록 설정 - listen
- 전화를 받을 수 있는 상태에 비유할 수 있다. like 전화 케이블 연결
- 전화를 거는 용도의 소켓은 연결요청이 가능한 상태의 소켓이 될 필요가 없다.
- 소켓에 할당된 IP와 PORT 번호로 연결요청이 가능한 상태가 된다.
- 연결 요청이 가능한 상태의 소켓으로 변환해주는 코드 (클라이언트의 접속 요청 대기)
#include <sys/socket.h>
int listen(int sockfd, int backlog);
- 성공 시 0, 실패 시 -1 반환
- 수화기를 드는 상황 - accept
- 연결요청의 수락. (클라이언트의 접속 허용)
- 연결요청이 수락되어야 데이터의 송수신이 가능하다
- 수락된 이후에 데이터의 송수신은 양방향으로 가능하다
- 연결요청 가능한 상태로 변경해주는 코드이다
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 성공 시 파일 디스크립터, 실패 시 -1 반환
- 연결요청이 있을 때에만 accept 함수가 반환을 한다
- 정리
- 소켓의 생성 - socket 함수 호출
- IP와 PORT번호의 할당 - bind 함수 호출
- 연결 요청 가능상태로 변경 - listen 함수 호출
- 연결 요청에 대한 수락 - accept 함수 호출
- bind, listen, accept - 서버측에서만 사용
- connect - 클라이언트 측에서만 사용
- Server code
- 연결요청을 허용하는 프로그램
- 클라이언트보다 먼저 실행되어야 한다
- 이렇게 생성된 소켓을 서버 소켓 또는 리스닝 소켓이라고 한다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len;
if (argc != 3) {
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0); // 소켓 생성
if (sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
error_handling("connect() error!");
str_len = read(sock, message, sizeof(message) - 1);
if (str_len == -1)
error_handling("read() error!");
printf("Message from server: %s \n", message);
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
- Client code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len;
if (argc != 3) {
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
//서버 프로그램에 연결을 요청
error_handling("connect() error!");
str_len = read(sock, message, sizeof(message) - 1);
if (str_len == -1)
error_handling("read() error!");
printf("Message from server: %s \n", message);
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
- server와 client 실행하기
<server>
gcc hello_server.c -o hserver
./hserver 9190
9190은 port
또 다른 terminal(Cygwin)을 열어서
<client>
gcc hello_client.c -o hclient
./hclient 127.0.0.1 9190
12.0.0.1은 로컬 컴퓨터의 IP이다.
* bind, listen 함수의 호출이 이어지면 서버 소켓, connect 함수의 호출로 이어지면 클라이언트 소켓이 되는 것이다.
출처: [열혈 TCP/IP 소켓 프로그래밍]
'Computer Networks > 실습' 카테고리의 다른 글
주소 할당 에러의 원인 - Time wait과 SO_REUSEADDR 옵션 (0) | 2019.11.08 |
---|---|
[CH.4-2,3] TCP기반 서버, 클리이언트의 구현 - Iterative 기반으로 구현 (0) | 2019.10.09 |
[CH.4-1] TCP와 UDP에 대한 이해 (0) | 2019.10.09 |
[실습] 주소체계와 데이터 정렬 - IP주소/PORT번호/주소정보 표현/인터넷 주소(Internet Address) (0) | 2019.09.25 |
리눅스 기반 파일 조작하기 (0) | 2019.09.25 |