멀티캐스트 (Multicast)
● 전송 방식
- UDP를 기반으로 하는 전송 방식
- 멀티캐스트 그룹을 기반으로 멀티캐스트 패킷을 주고 받음
- 하나의 멀티캐스트 패킷은 라우터를 통해서 다수의 호스트에 전송
멀티캐스트 전송의 특징
● Multicast
- 필요성
- 멀티미디어 데이터의 등장으로 일대다의 전송 방식이 필요하게 됨
- 유니캐스트 방식은 많은 클라이언트들의 연결을 감당하기 힘듬
- 브로드캐스트 방식은 같은 전송망의 모든 클라이언트에게 전송하는 단점
- LAN이나 WAN사이에서 이루어지는 멀티캐스트가 필요
- 가용 주소
- 224.0.0.0~239.255.255.255 에 걸쳐 사용되는 Class D가 멀티캐스트
- Class D 의 하위 28비트는 멀티캐스트 그룹 ID, 32비트는 그룹 주소
멀티캐스트 그룹
● 멀티캐스트 주소
- 멀티캐스트 그룹 호스트 주소 , 244.0.0.0 부터 239.255.255.255 [ D 그룹 ]
● 멀티캐스트 그룹
- 하나의 멀티캐스트 주소를 공유하는 인터넷 호스트들의 집합
- IGMP를 통해 그룹에 참여 또는 탈퇴
● 멀티캐스트 그룹 주소 신청
- IANA (Internet Assigned Numbers Authority), 인터넷 할당 번호 관리 기관
멀티캐스트 흐름
- 중간의 라우터들은 MRP (Multicast Routing Protocol)을 실행
- 임의의 호스트가 멀티캐스트 그룹에 가입하면 호스트는 인접한 멀티캐스트 라우터에게 IGMP 메시지를 보내어 멀티캐스트 그룹에 가입된 사실 통보 , IGMP(Internet Group Management Protocol) : ~복수의 호스트에 UDP 데이터 그램을 송신하는 멀티캐스팅에 이용 되는 프로토콜
- 멀티캐스트 패킷의 송수신은 UDP 소켓 아용, 비연결형으로 패킷의 분실, 순서 바뀜 등을 확인해주지 않는다.
- 멀티캐스트 그룹에 속하지 않은 호스트에서도 멀티캐스트 패킷을 전송할 수 있다.
LAN
- 송신 측은 목적지 주소와 포트를 가지고 해당 호스트로 데이터 전송
- 수신 측은 자신의 주소로 대기하지 않고 멀티캐스트 그룹주소로 대기함
- 해당 멀티캐스트 그룹 주소에 포함된 주소로만 데이터 송신 작업
WAN
- 송신 측이 속한 LAN에 대하여 우선적으로 처리
- 멀티캐스트 라우터 사이는 MRP를 이용하여 데이터 전송 처리
- 외부 LAN으로 전송 시 전송 데이터의 복사본을 만들어 라우터로 전송
- MRP = Multicast Routing Protocol
멀티캐스트 채팅 프로그램
멀티캐스트는 서버/클라이언트가 동일
- 멀티 캐스트 그룹 주소 설정 작업
- 데이터 송수신 소켓 생성 (UDP)
- 생성한 소켓의 옵션 설정
- 설정한 소켓 연동(BIND)
- 데이터 송수신
- 소켓 종료(CLOSE)
- 프로그램 종료
멀티캐스트의 구현
- Sender : 임의의 멀티캐스트 그룹에 데이터를 전송하는 호스트
- Receiver : 임의의 멀티캐스트 그룹으로부터 데이터를 수신하는 호스트
Sender | Receiver |
|
|
멀티캐스트와 관련된 소켓 옵션 지정
- IP_ADD_MEMBERSHIP : 멀티캐스트 그룹에 가입
- IP_DROP_MEMBERSHIP : 멀티캐스트 그룹에서 탈퇴
- IP_MULTICAST_TTL : 멀티캐스트 패킷의 loopback 허용 여부 지정
- IP_MULTICAST_IF : 멀티캐스트 패킷의 TTL 값 지정
멀티캐스트 그룹 주소를 나타내는 구조체
struct ip_mreq{
struct in_addr imr_multiaddr; //클래스 D의 ip 주소
sturct in_addr imr_interface; //자신의 ip 주소
setsockopt(send_s, IPPROTO_IP, IP_MULTICAST_LOOP, &no, sizeof(no));
사용예시) ./multicast 224.1.1.1 3333 kim
multicast.c
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define MAXLINE 1024
int main(int argc, char *argv[])
{
int send_s, recv_s;
unsigned int yes = 1;
struct sockaddr_in mcast_group;
struct ip_mreq mreq;
char line[MAXLINE], name[10]; /* 채팅 참가자 이름 */
int n, len;
pid_t pid;
if (argc < 4)
{
printf("usage : %s ip_address port_number user_id\n", argv[0]);
return -1;
}
sprintf(name, "[%s]", argv[3]);
/* 멀티캐스트 수신용 소켓 개설 */
memset(&mcast_group, 0, sizeof(mcast_group));
mcast_group.sin_family = AF_INET;
mcast_group.sin_port = htons(atoi(argv[2]));
mcast_group.sin_addr.s_addr = inet_addr(argv[1]);
if ((recv_s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("error: Can't create receive socket\n");
return -1;
}
mreq.imr_multiaddr = mcast_group.sin_addr; /* 멀티캐스트 그룹에 가입 */
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(recv_s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
{
perror("error: add membership\n");
return -1;
}
if (setsockopt(recv_s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
{
perror("error : reuse setsockopt\n");
return -1;
}
if (bind(recv_s, (struct sockaddr *)&mcast_group, sizeof(mcast_group)) < 0)
{
perror("error : bind receive socket\n");
return -1;
}
if ((send_s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("error : Can't create send socket\n");
return -1;
}
if ((pid = fork()) < 0)
{
perror("error : fork\n");
return -1;
}
else if (pid == 0)
{ /* child process : 채팅 메시지 수신 담당 */
struct sockaddr_in from;
char message[MAXLINE + 1];
for (;;)
{
len = sizeof(from);
if ((n = recvfrom(recv_s, message, MAXLINE - 2, 0, (struct sockaddr *) & from, &len)) < 0)
{
perror("error : recvfrom\n");
return -1;
}
printf("receiving message...: %s\n", message);
};
}
else
{ /* parent process : 키보드 입력 및 메시지 송신 담당 */
char message[MAXLINE + 1], line[MAXLINE + 1];
printf("Send Message :");
while (fgets(message, MAXLINE, stdin) != NULL)
{
sprintf(line, "%s %s", name, message);
if (sendto(send_s, line, strlen(line), 0,
(struct sockaddr *)&mcast_group, sizeof(mcast_group)) < strlen(line))
{
printf("error : sendto\n");
return -1;
}
};
}
return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs |
브로드캐스트
UDP를 기반으로 하는 전송 방식 (멀티캐스트와 같다)
- 일반적인 UDP 패킷과의 차이점은 전송 목적지 IP주소 뿐이다.
- 동일 네트워크에 속하는 모든 호스트에 동시 전송(멀티캐스트와의 차이점)
news_sender_brd.c
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc,char *argv[])
{
int send_sock;
struct sockaddr_in broad_adr;
FILE *fp;
char buf[BUF_SIZE];
int so_brd=1;
if(argc!=3){
printf("Usage: %s <Broadcast IP> <PORT>\n",argv[0]);
exit(1);
}
send_sock=socket(PF_INET,SOCK_DGRAM,0);
memset(&broad_adr,0,sizeof(broad_adr));
broad_adr.sin_family=AF_INET;
broad_adr.sin_addr.s_addr=inet_addr(argv[1]);
broad_adr.sin_port=htons(atoi(argv[2]));
setsockopt(send_sock,SOL_SOCKET,
SO_BROADCAST,(void*)&so_brd,sizeof(so_brd));
error_handling("fopen() error");
while(!feof(fp))
{
fgets(buf,BUF_SIZE,fp);
sendto(send_sock,buf,strlen(buf),0,
(struct sockaddr*)&broad_adr,sizeof(broad_adr));
sleep(2);
}
close(send_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs |
news_receiver_brd.c
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 30
void error_handling(char *message);
int main(int argc,char *argv[])
{
int recv_sock;
struct sockaddr_in adr;
int str_len;
char buf[BUF_SIZE];
if(argc!=2){
printf("Usage: %s <PORT>\n",argv[0]);
exit(1);
}
recv_sock=socket(PF_INET,SOCK_DGRAM,0);
memset(&adr,0,sizeof(adr));
adr.sin_family=AF_INET;
adr.sin_addr.s_addr=htonl(INADDR_ANY);
adr.sin_port=htons(atoi(argv[1]));
if(bind(recv_sock,(struct sockaddr*)&adr,sizeof(adr))==-1)
error_handling("bind() error");
while(1)
{
str_len=recvfrom(recv_sock,buf,BUF_SIZE-1,0,NULL,0);
if(str_len<0)
break;
buf[str_len]=0;
fputs(buf,stdout);
}
close(recv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs |
멀티쓰레드 기반의 서버구현
쓰레드의 등장배경
프로세스는 부담스럽다.
- 프로세스의 생성에는 많은 리소스가 소모
- 일단 프로세스가 생성되면, 프로세스 간의 컨텍스트 스위칭으로 인해서 성능이 저하
- 컨텍스트 스위칭은 프로세스의 정보를 하드디스크에 저장 및 복원하는 일이다.
데이터의 교환이 어렵다.
- 프로세스간 메모리가 독립적으로 운영되기 때문에 프로세스간 데이터 공유 불가능!
- 따라서 운영체제가 별도로 제공하는 메모리 공간을 대상으로 별도의 IPC 기법 적용
그렇다면 쓰레드는?
- 프로세스보다 가벼운, 경량화된 프로세스이다. 컨텍스트 스위칭이 빠름
- 쓰레드 별로 메모리 공유가 가능, 별도의 IPC 기법 불필요
- 프로세스 내에서의 프로그램의 흐름을 추가한다.
운영체제와 프로세스, 쓰레드의 관계
쓰레드 생성에 사용되는 함수
#include <pthread.h>
int pthread_create (
pthread_t *restrict thread, const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg
); //성공시 0, 실패시 0의외의 값 반환
- thread : 생성할 쓰레드의 ID 저장을 위한 변수의 주소 값 전달, 참고로 쓰레드는 프로세스와 마찬가지로 쓰ㅔ드의 구분을 위한 ID가 부여 된다.
- attr : 쓰레드에 부여할 특성 정보의 전달을 위한 매개변수, NULL 전달 시 기본적인 특성의 쓰레드가 생성
- start_routine : 쓰레드의 main 함수 역할을 하는, 별도 실행흐름의 시작이 되는 함수의 주소값(함수포인터) 전달.
- arg : 세 번째 인자를 통해 등록된 함수가 호출될 때 전달할 인자의 정보를 담고 있는 변수의 주소 값 전달.
gcc thread1.c -o tr1 -lpthread
thread1.c
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
29
30
31
|
#include <stdio.h>
#include <pthread.h>
void* thread_main(void *arg);
int main(int argc,char *argv[])
{
pthread_t t_id;
int thread_param=5;
if(pthread_create(&t_id,NULL,thread_main,(void*)&thread_param)!=0)
{
puts("pthread_create() error");
return -1;
};
sleep(10); puts("end of main");
return 0;
}
void* thread_main(void *arg)
{
int i;
int cnt=*((int*)arg);
for(i=0;i<cnt;i++)
{
sleep(1),puts("running thread");
}
return NULL;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs |
thread_main 함수가 쓰레드의 main 함수. 이를 가리켜 쓰레드 함수라 한다.
쓰레드가 생성되면 생성된 쓰레드는 쓰레드 함수를 실행한다. 쓰레드 함수의 실행이 완료되면 쓰레드는 종료된다.
쓰레드의 종료를 대기
#include <pthread.h>
int pthread_join(pthread_t thread, void **status); //성공 시 0, 실패 시 0 이외의 값 반환
- thread : 이 매개변수에 전달되는 ID의 쓰레드가 종료될 때까지 함수는 반환하지 않는다.
- status : 쓰레드의 main 함수가 반환하는 값이 저장될 포인터 변수의 주소 값을 전달한다.
첫 번째 인자로 전달되는 ID의 쓰레드가 종료될 때까지, 이 함수를 호출한 프로세스(또는 쓰레드)를 대기상태에 둔다.
pthread_join 함수의 호출 예
thread2.c
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
void* thread_main(void *arg);
int main(int argc, char *argv[])
{
pthread_t t_id;
int thread_param=5;
void * thr_ret;
if(pthread_create(&t_id, NULL, thread_main, (void*)&thread_param)!=0)
{
puts("pthread_create() error");
return -1;
};
if(pthread_join(t_id, &thr_ret)!=0)
{
puts("pthread_join() error");
return -1;
};
printf("Thread return message: %s \n", (char*)thr_ret);
free(thr_ret);
return 0;
}
void* thread_main(void *arg)
{
int i;
int cnt=*((int*)arg);
char * msg=(char *)malloc(sizeof(char)*50);
strcpy(msg, "Hello, I'am thread~ \n");
for(i=0; i<cnt; i++)
{
sleep(1); puts("running thread");
}
return (void*)msg;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs |
임계영역내에서 호출이 가능한 함수
쓰레드에 안전한 함수, 쓰레드에 불안전한 함수
- 둘 이상의 쓰레드가 동시에 호출하면 문제를 일으키는 함수를 가리켜 쓰레드에 불안전한 함수 (Thread-unsafe function) 라 한다.
- 둘 이상의 쓰레드가 동시에 호출을 해도 문제를 일으키지 않는 함수를 가리켜 쓰레드에 안전한 함수 (Thread-safe function) 라 한다.
쓰레드의 동기화
동기화의 두 가지 측면과 동기화 기법
동기화가 필요한 대표적인 상황
- 동일한 메모리 영역으로의 동시접근이 발생하는 상황
- 동일한 메모리 영역에 접근하는 쓰레드의 실행순서를 지정해야 하는 상황
동기화를 통해서 동시접근을 막을 수 있고, 게다가 접근의 순서를 지정하는 것도 가능
동기화 기법
- 뮤텍스(Mutex) 기반 동기화
- 세마포어(Semaphore) 기반 동기화
동기화는 운영체제가 제공하는 기능이기 때문에 운영체제에 따라서 제공되는 기법 및 적용의 방법에 차이가 있다.
뮤텍스 기반의 동기화
뮤텍스의 생성과 소멸
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
- mutex : 뮤텍스 생성시에는 뮤텍스의 참조 값 저장을 위한 변수의 주소 값 전달, 그리고 뮤텍스 소멸 시에는 소멸하고자 하는 뮤텍스의 참조 값을 저장하고 있는 변수의 주소 값 전달
- attr : 생성하는 뮤텍스의 특성정보를 담고 있는 변수의 주소 값 전달, 별도의 특성을 지정하지 않을 경우에는 NULL 전달.
뮤텍스의 획득과 반환
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex); //임계영역의 시작
int pthread_mutex_unlock(pthread_mutex_t *mutex); //임계영역의 끝
mutex.c
gcc mutex.c -D_REENTRANT -o mutex -lpthread
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREAD 100
void * thread_inc(void * arg);
void * thread_des(void * arg);
long long num = 0;
pthread_mutex_t mutex;
int main(int argc,char *argv[])
{
pthread_t thread_id[NUM_THREAD];
int i;
pthread_mutex_init(&mutex,NULL);
for(i=0;i<NUM_THREAD; i++)
{
if(i%2)
pthread_create(&(thread_id[i]),NULL,thread_inc,NULL);
else
pthread_create(&(thread_id[i]),NULL,thread_des,NULL);
}
for(i=0;i<NUM_THREAD;i++)
pthread_join(thread_id[i],NULL);
printf("result:%lld \n",num);
pthread_mutex_destroy(&mutex);
return 0;
}
void * thread_inc(void * arg)
{
int i;
pthread_mutex_lock(&mutex);
for(i=0;i<500000;i++)
num+=1;
pthread_mutex_unlock(&mutex);
return NULL;
}
void * thread_des(void * arg)
{
int i;
for(i=0;i<500000;i++)
{
pthread_mutex_lock(&mutex);
num-=1;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
|
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs |
mutex의 lock과 unlock의 함수호출 횟수는 최소화 하는게 성능에 유리
HTTP
● 기능
- WWW(World Wide Web) 상의 데이터 접근 시 사용되는 프로토콜 (TCP 80번 포트 사용)
- 하나의 페이지에서 다른 페이지로 이동하는 하이퍼텍스트 환경에서 효과적으로 이용됨
● 상태가 존재하지 않는 stateless 프로토콜
- HTTP는 TCP기반의 프로토콜
- 단순 응답 형식의 프로토콜
● 요청 메시지는 요청 라인, 헤더 정보, 메시지 바디, 총 3부분으로 구성
● 응답 메시지는 상태 라인, 헤더 정보, 메시지 바디로 3부분으로 구성
HTTP 메시지 구조
● 요청 메시지 : 정보, 자료 요청
- 요청 라인, 헤더, 공백 라인, 본문으로 구성
● 응답 메시지 : 요청에 대한 응답
- 상태라인, 헤더, 공백라인, 본문으로 구성
요청 라인 (Request Line)
- 메소드 : 서비스 수행 기능 명시
- URL : 웹페이지 위치 정보
- HTTP 버전 : HTTP 프로토콜 버전 정보 (현재 v1.1) , <major>.<minor>
상태 라인 (Status Line)
- HTTP 버전 : 응답 메시지에서 사용하는 HTTP 버전
- 상태 코드 : 요청 수락 및 수행 시도의 결과를 나타내는 3자리의 십진수 코드
- 상태 설명 : 사용자를 위한 상태 코드의 설명
---------------------------------------오후 실습-----------------------------------------------
mysql
php
apache2
https://webnautes.tistory.com/1206
윈도우 기반 웹 개발 환경 만들기 ( Apache2, PHP, MySQL, PhpMyAdmin )
윈도우에 Apache2 + PHP + MySQL 조합으로 웹서버를 쉽게 구축할 수 있게 도와주는 WampServer 설치 및 사용방법을 다루고 있습니다. 2018. 7 .12 - 최초 작성 2019. 5. 16 - 업데이트 : 윈도우 10에서 외부접속이..
webnautes.tistory.com
https://webnautes.tistory.com/828
Android PHP MySQL 예제 - 데이터베이스에 데이터 입력하기
안드로이드 앱이 PHP 프로그램을 매개로 하여 MySQL 데이터베이스 서버에 데이터를 저장하는 간단한 예제입니다. 1. Apache2, MySQL, PHP7 설치 2. 데이터베이스 및 테이블 생성 3. 웹브라우저로 PHP 동작 테스트..
webnautes.tistory.com
'딥러닝 기반 영상인식 개발 전문가 과정 > 리눅스' 카테고리의 다른 글
6월18일 mysql, php 실습 (0) | 2019.06.18 |
---|---|
6월18일 멀티쓰레드 기반의 다중접속 서버의 구현 (0) | 2019.06.18 |
6월14일 epoll, 레벨트리거, 엣지트리거, 멀티캐스트 (0) | 2019.06.14 |
6월14일 채팅실습, fileno, fdopen, 입력 스트림과 출력 스트림의 분리 (0) | 2019.06.14 |
6월13일 채팅, 실습 (0) | 2019.06.13 |