멀티캐스트 (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

 

멀티캐스트 채팅 프로그램

멀티캐스트는 서버/클라이언트가 동일

  1. 멀티 캐스트 그룹 주소 설정 작업 
  2. 데이터 송수신 소켓 생성 (UDP)
  3. 생성한 소켓의 옵션 설정
  4. 설정한 소켓 연동(BIND)
  5. 데이터 송수신
  6. 소켓 종료(CLOSE)
  7. 프로그램 종료

 

멀티캐스트의 구현

  • Sender : 임의의 멀티캐스트 그룹에 데이터를 전송하는 호스트
  • Receiver : 임의의 멀티캐스트 그룹으로부터 데이터를 수신하는 호스트
Sender Receiver
  • UDP 소켓 생성.
  • TTL 설정(소켓 옵션 설정).
  • 멀티캐스트 그룹으로 데이터 전송.
  • UDP 소켓 생성.
  • 멀티캐스트 그룹 지정(ip_mreq 구조체).
  • 멀티캐스트 그룹 가입(소켓 옵션 설정).

 

 

멀티캐스트와 관련된 소켓 옵션 지정

  • 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, 0sizeof(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 - 20, (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));
 
    if((fp=fopen("news.txt","r"))==NULL)
        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

 

+ Recent posts