제14강 소켓프로그램통한 원격제어 · 2019-08-15 · raspberrypi 소켓프로그래밍...
TRANSCRIPT
RaspberryPi 소켓프로그래밍 1
제14강
소켓프로그램통한 원격제어
소켓 프로그래밍
TCP 방식의 서버/클라이언트 프로그래밍
TCP 방식에 의한 LED 원격 제어
UDP 방식의 서버/클라이언트 프로그래밍
UDP 방식에 의한 LED 원격 제어
응용과제
참고) 포트 포워딩
RaspberryPi 소켓프로그래밍 5
소켓프로그래밍(계속)
* socket() : 소켓 생성 함수
#include <sys/socket.h>
#include <sys/types.h>
int socket(int domain, int type, int protocol);
domain : AF_INET(address family) or PF_INET(protocol family), ...
type : SOCK_STREAM(연결형,TCP) / SOCK_DGRAM(비연결형,UDP)
protocol : 0 or IPPROTO_TCP / IPPROTO_UDP
예) TCP 소켓
sock = socket(AF_INET, SOCK_STREAM, 0);
예) UDP 소켓
sock = socket(AF_INET, SOCK_DGRAM, 0);
RaspberryPi 소켓프로그래밍 6
소켓프로그래밍(계속)
* 주소정보를 위한 구조체
■ sockaddr 구조체 : 소켓 주소를 표현하는 범용의 구조체
struct sockaddr { sa_family_t sa_family; // 소켓의 프로토콜 주소체계 char sa_data[14]; // 해당 주소체계에서 사용하는 주소정보
}
■ sockaddr_in 구조체
: IPv4 주소체계에서 사용하는 소켓 주소 표현 구조체
struct sockaddr_in { sin_family_t sin_family; // IPv4 주소체계, AF_INET uint16_t sin_port; // 포트 번호, 16bit, Big Endian struct in_addr sin_addr; // IPv4 IP 주소 구조체, 32bit, Big Endian char sin_zero[8]; // sockaddr과 같은 크기위해, 항상 0
}
struct in_addr { uint32_t s_addr; // IPv4 IP 주소, 32bit, Big Endian}
RaspberryPi 소켓프로그래밍 7
소켓프로그래밍(계속)
■ 서버 사용예
struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address));server_address.sin_family =AF_INET;server_address.sin_port =htons(PORT); server_address.sin_addr.s_addr =INADDR_ANY;// htons()(host to network short) : Big Endian방식으로 변환// INADDR_ANY : 실행중인 시스템 IP 주소에 대한 매크로
■ 클라이언트 사용예
struct sockaddr_in client_address;
memset(&client_address, 0, sizeof(client_address));client_address.sin_family =AF_INET;client_address.sin_port =htons(PORT); client_address.sin_addr.s_addr =inet_addr("192.168.0.40"); // 서버IP// inet_addr() : IP 주소의 점십진표기 문자열을 수치화, Big Endian방식
: 범용의 소켓 주소 구조체 sockaddr로 형변환하여 사용함
(struct sockaddr *)&server_addr
RaspberryPi 소켓프로그래밍 8
소켓프로그래밍(계속)
* bind()
: 통신위해 소켓기술자와 소켓주소 구조체 연결
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, int addrlen);
sockfd : socket()이 생성한 소켓 기술자
*myaddr : 소켓 번호와 연결할 소켓 주소 구조체 포인터
addrlen : 소켓 주소 구조체 크기
리턴값 : 정상 0, 에러 -1
예) bind(sockfd, (struct sockaddr *)&server_address,
sizeof(server_address));
// 구조체를 sockaddr 범용 구조체로 형변환
RaspberryPi 소켓프로그래밍 9
소켓프로그래밍(계속)
* listen()
: 클라이언트로부터의 가용한 연결요청 대기큐의 크기 설정
: 대기큐에서의 요청순서에 따라 서비스. 병행처리아님
int listen(int sockfd, int n)
sockfd : socket()이 생성한 소켓 기술자
n : 대기큐의 길이, 클라이언트 연결 요청 최대 수
리턴값 : 성공했을 경우 0, 실패했을 경우 -1을 리턴
예)
listen(sockfd, 2);
: 서버가 최대 2개의 connect() 요청을 대기시킬 수 있으며,
세번째 이후의 connect() 요청은 거절하여 클라이언트가 알게 함
RaspberryPi 소켓프로그래밍 10
소켓프로그래밍(계속)
* accept()
: 클라이언트의 주소정보를 반환하여 통신 채널 생성
int accept(int sockfd, struct sockaddr *addr, int *addrlen)
sockfd : socket()이 생성한 소켓 기술자
addr : 접속을 허가 받은 클라이언트 sockaddr 구조체 포인터
addrlen : 클라이언트의 sockaddr 구조체의 크기를 받을 포인터
리턴값 : 성공시 생성된 클라이언트의 소켓 기술자, 실패시 -1
RaspberryPi 소켓프로그래밍 11
소켓프로그래밍(계속)
* read() / write()
: TCP 방식에서의 송수신
int write(sockfd, char *buf, int bufsize)
int read(sockfd, char *buf, int bufsize)
sockfd : 서버의 경우 accept()에 의해 생성된 소켓 기술자
클라이언트의 경우 자신의 소켓 기술자
buf : 송수신할 데이터 버퍼
bufsize : 버퍼의 크기
리턴값 : 송수신 문자수, 실패시 -1
: UDP 방식에서는
sendto(), recvfrom() 함수 사용
RaspberryPi 소켓프로그래밍 12
소켓프로그래밍(계속)
* connect() : 클라이언트 측에서 연결요청
: 소켓에 클라이언트의 IP주소, 포트번호를 자동 할당하고,
: TCP 방식의 클라이언트가 서버로의 연결 요청하는 함수
: 사전에 연결할 서버의 주소 구조체 초기화 필요
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd : 클라이언트의 소켓
serv_addr : 서버의 주소 정보를 담고 있는 구조체
addr_len : serv_addr 구조체의 길이
RaspberryPi 소켓프로그래밍 13
소켓프로그래밍(계속)
* close()
: TCP방식에서는 미처리된 패킷들을 모두 처리한 후에 소켓을 닫음
예외) 미처리 패킷을 즉시 버리거나, 지정 시간동안 처리위해
setsockopt() 사용
: UDP방식에서는 단순히 소켓을 닫는 작업
close(sockfd);
RaspberryPi 소켓프로그래밍 16
TCP 방식(계속)
* Linux TCP Server 소스
: 서버의 포트번호로 0x5005를 사용
$ nano tcpServer.c
//=======================================// tcpSever.c (Linux)// for Chatting//=======================================#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <netinet/in.h>
#define PORTNUM 0x5005 // port#define BUFFSIZE 256
int main(void) {struct sockaddr_in serv_addr, cli_addr;int serv_fd, cli_fd, clilen;char buffer[BUFFSIZE];
RaspberryPi 소켓프로그래밍 17
printf("[TCP server for chatting...]\n");
// 1) create server socketif((serv_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("ERROR opening socket");exit(1);
}
// 2) setting server socket structurememset((char *) &serv_addr, 0x00, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(PORTNUM);
// 3) bind()if(bind(serv_fd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr)) == -1) { perror("ERROR on binding");exit(1);
}
// 4) listen()listen(serv_fd, 5);
loop:
// 5) accept(), blocking...
RaspberryPi 소켓프로그래밍 18
clilen = sizeof(cli_addr);if((cli_fd = accept(serv_fd, (struct sockaddr *)&cli_addr,
&clilen)) == -1) {perror("ERROR on accept");exit(1);
}
// 6) read/write, write(), sending...write(cli_fd, "Welcome to Chat Server.....", BUFFSIZE);
while(1) {memset(buffer, 0x00, sizeof(buffer));
// 6) read/write, read(), receiving...if((read(cli_fd, buffer, BUFFSIZE)) == -1) {
perror("ERROR reading from socket");exit(1);
}printf("[Guest] %s\n", buffer);
if(buffer[0] == 'q') {close(cli_fd);goto loop;
}
memset(buffer, 0x00, sizeof(buffer));printf("[Server] ");fgets(buffer, BUFFSIZE, stdin);
RaspberryPi 소켓프로그래밍 19
// 6) read/write, write(), sending...write(cli_fd, buffer, BUFFSIZE);if(buffer[0] == 'q')
break;}
// 7) close(), close server socker, disconnectionclose(serv_fd);
return 0;}
RaspberryPi 소켓프로그래밍 20
TCP 방식(계속)
* Linux TCP Client 소스
: 서버의 IP 주소를 SERVERIP 매크로에 지정
$ make tcpClient.c
//=======================================// tcpClient.c (Linux)// for Chatting//=======================================#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <netinet/in.h>
#define SERVERIP "192.168.0.40" // taget !!#define PORTNUM 0x5005 // port#define BUFFSIZE 256
int main(void) {int sock_fd;struct sockaddr_in serv_addr;
RaspberryPi 소켓프로그래밍 21
char buffer[BUFFSIZE];
printf("[TCP server for chatting...]\n");
// 1) create client socketif((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("ERROR opening socket");exit(1);
}
// 2) setting server socket structureserv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(SERVERIP);serv_addr.sin_port = htons(PORTNUM);
// 3) connectif(connect(sock_fd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr)) == -1){perror("ERROR connecting");exit(1);
}
// 4) read/write, read(), receiving...memset(buffer, 0x00, sizeof(buffer));if(read(sock_fd, buffer, BUFFSIZE)== -1){
perror("ERROR reading from socket");exit(1);
}
RaspberryPi 소켓프로그래밍 22
printf("[Server] %s\n", buffer);
while(1) {memset(buffer, 0x00, sizeof(buffer));
printf("[Guest] ");fgets(buffer, BUFFSIZE, stdin);
// 4) read/write, write(), sending...if(write(sock_fd, buffer, strlen(buffer)) == -1) {
perror("ERROR writing to socket");exit(1);
}if(buffer[0] == 'q')
break;
// 4) read/write, read(), receiving...memset(buffer, 0x00, sizeof(buffer));if(read(sock_fd, buffer, BUFFSIZE) == -1) {
perror("ERROR reading from socket");exit(1);
}
printf("[Server] %s\n", buffer);if(buffer[0] == 'q')
break;}
RaspberryPi 소켓프로그래밍 24
TCP 방식(계속)
[실습1] TCP 서버/클라이언트 I
: 앞의 서버측 및 클라이언트 측 소스를 활용
: 소스 컴파일
서버 측 $ make tcpServer
클라이언트 측 $ make tcpClient
: 리눅스 시스템간 동작을 확인
서버 측 $ ./tcpServer
클라이언트 측 # ./tcpClient
RaspberryPi 소켓프로그래밍 25
TCP 방식(계속)
* Windows 소켓 프로그래밍
: 소스에는 winsock2.h 파일을 포함
: 윈도우 소켓의 버전과 해당 버전의 라이브러리를 초기화
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {perror("ERROR WSAStartup()");exit(1);
}
// MAKEWORD 매크로 함수 사용, 주번호 2, 부번호 2의 소켓 버전 사용
// 해당버전의 윈속 라이브러리를 사용하겠다고 초기화
: 초기화된 라이브러리 해제
WSACleanup(); // 통상 프로그램의 종료 직전에 위치
: 기타 소켓 관련 함수들은 리눅스 환경과 동일
예외!!!) 소켓을 닫을 때 close() 대신 closesocket() 함수를 사용
RaspberryPi 소켓프로그래밍 26
TCP 방식(계속)
* Windows TCP Client 소스
: 범용성을 위해 서버의 IP 주소를 명령행 인자로 전달
: 사용 포트 번호는 0x5005
//=======================================// tcpClientWin.c (Windows)// for Chatting//=======================================#include <stdio.h>#include <string.h>#include <stdlib.h>#include <winsock2.h>
//#define SERVERIP "192.168.0.40" // taget !!#define PORTNUM 0x5005 //#define BUFFSIZE 256
int main(int argc, char *argv[]) {SOCKET sock_fd;SOCKADDR_IN serv_addr;WSADATA wsaData;
RaspberryPi 소켓프로그래밍 27
char buffer[BUFFSIZE];
if(argc != 2) { printf("usage : %s [port]\n", argv[0]);
exit(0);}
printf("[TCP client for chatting...]\n");
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {perror("ERROR WSAStartup()");exit(1);
}
// 1) create client socketif((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("ERROR opening socket");exit(1);
}
// 2) setting server socket structureserv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // SERVERIPserv_addr.sin_port = htons(PORTNUM);
// 3) connectif(connect(sock_fd, (SOCKADDR *)&serv_addr,
sizeof(serv_addr)) == -1) {
RaspberryPi 소켓프로그래밍 28
perror("ERROR connecting");exit(1);
}
// 4) read/write, read(), receiving...memset(buffer, 0x00, sizeof(buffer));if(recv(sock_fd, buffer, BUFFSIZE, 0) == -1) {
perror("ERROR reading from socket");exit(1);
} printf("[Server] %s\n", buffer);
while(1) {memset(buffer, 0x00, sizeof(buffer));printf("[Guest] ");fgets(buffer, BUFFSIZE, stdin);
// 4) read/write, write(), sending...if(send(sock_fd, buffer, strlen(buffer), 0) == -1) {
perror("ERROR writing to socket");exit(1);
}if(buffer[0] == 'q')
break;
// 4) read/write, read(), receiving...memset(buffer, 0x00, sizeof(buffer));if(recv(sock_fd, buffer, BUFFSIZE, 0) == -1) {
RaspberryPi 소켓프로그래밍 29
perror("ERROR reading from socket");exit(1);
}
printf("[Server] %s\n", buffer);if(buffer[0] == 'q')
break;}
// 5) close(), close client socker, disconnectionclosesocket(sock_fd);
WSACleanup();
return 0;}
RaspberryPi 소켓프로그래밍 30
TCP 방식(계속)
[실습2] TCP 서버/클라이언트 II
: Visul Studio에서 ( 프로젝트 이름을 tcpClientWin )
: 소스 편집창에 위 소스 복사
: 윈속 프로그램 컴파일시 조치사항
// 윈속(winsock) 라이브러리 ws2_32.lib를 링크토록
-단축키 Alt-F7 키를 눌러
-프로젝트의 속성 페이지 창에서 ( 아래 좌측 그림 )
-좌측화면의 ‘구성속성 – 링커 – 입력’ 클릭
-우측 화면의 ‘추가 종속성’ 항목 클릭
-우측 끝의 역삼각형의 버튼을 클릭
-편집항목을 선택
-다음의 화면(아래 우측 그림)에서 ws2_32.lib를 입력하고
RaspberryPi 소켓프로그래밍 32
: 리눅스 서버와 Windows 클라이언트 실행
서버 측 $ ./tcpServer_01
클라이언트 측 c:\> tcpClientWin 192.168.0.40
// 명령행에서 서버의 IP 주소 사용
: 동작 결과
- 서버와 클라이언트 간에 교대로 메시지를 송수신 가능
- 접속 종료 시 첫 글자를 q로 시작
RaspberryPi 소켓프로그래밍 34
TCP 방식(계속)
* Linux TCP Server 소스 ( LED 제어용 )
: LED 회로는 wiringPi 핀 번호체계 #1로 연결 구성
: 기존 TCP 서버 소스 + LED 디바이스를 제어(청색부분)
$ nano tcpServer_01.c
//=======================================// tcpSever_01.c (Linux)// Chatting + controlling LED device//=======================================#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <netinet/in.h>
#include <wiringPi.h> //
#define PORTNUM 0x5005 // port##define BUFFSIZE 256
RaspberryPi 소켓프로그래밍 35
#define P_LED 1 // BCM_GPIO #18
int main(void) {struct sockaddr_in serv_addr, cli_addr;int serv_fd, cli_fd, clilen;char buffer[BUFFSIZE];
printf("[TCP server for chatting and controlling LED...]\n");
// 1) create server socketif((serv_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("ERROR opening socket");exit(1);
}
// 2) setting server socket structurememset((char *) &serv_addr, 0x00, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(PORTNUM);
// 3) bind()if(bind(serv_fd, (struct sockaddr *)&serv_addr,
sizeof(serv_addr)) == -1) { perror("ERROR on binding");exit(1);
}
RaspberryPi 소켓프로그래밍 36
// 4) listen()listen(serv_fd, 5);
loop:
// 5) accept(), blocking...clilen = sizeof(cli_addr);if((cli_fd = accept(serv_fd, (struct sockaddr *)&cli_addr,
&clilen)) == -1) {perror("ERROR on accept");exit(1);
}
// 6) read/write, write(), sending...write(cli_fd, "Welcome to Chat Server.....LED control..", BUFFSIZE);
if(wiringPiSetup() == -1) // wiringPi pin #return 1;
pinMode(P_LED, OUTPUT); //
while(1) {// 6) read/write, read(), receiving...memset(buffer, 0x00, sizeof(buffer));if((read(cli_fd, buffer, BUFFSIZE)) == -1){
perror("ERROR reading from socket");exit(1);
}
RaspberryPi 소켓프로그래밍 37
printf("[Guest] %s\n", buffer);
if(buffer[0] == 'q') {close(cli_fd);goto loop;
}else if(buffer[0] == '0') // LED control....
digitalWrite(P_LED, LOW);else if(buffer[0] == '1')
digitalWrite(P_LED, HIGH);
// 6) read/write, write(), sending...memset(buffer, 0x00, sizeof(buffer));printf("[Server] ");fgets(buffer, BUFFSIZE, stdin);write(cli_fd, buffer, BUFFSIZE);if(buffer[0] == 'q')
break;}
// 7) close(), close server socker, disconnectionclose(serv_fd);
return 0;}
: 컴파일 $ make tcpServer_01
RaspberryPi 소켓프로그래밍 38
TCP 방식(계속)
[실습3] TCP 서버/클라이언트에 의한 LED 제어
: 클라이언트는 기존 Linux용, 혹은 Windows용 사용
1) 리눅스 시스템간 동작을 확인
서버 측 $ ./tcpServer_01
클라이언트 측 # ./tcpClient
2) 리눅스 서버와 Windows 클라이언트간 동작을 확인
서버 측 $ ./tcpServer_01
클라이언트 측 c:\> tcpClientWin 192.168.0.40
RaspberryPi 소켓프로그래밍 39
: 동작 결과
- 클라이언트 측에서 전송하는 문자열 중 첫 문자가 0 혹은 1에 따라
- 서버측 LED 디바이스를 OFF 혹은 ON으로 제어
RaspberryPi 소켓프로그래밍 42
UDP 방식(계속)
* sendto() / recvfrom()
: UDP 방식에서의 송수신 함수
: 매 패킷에 송수신 주소 정보 포함하여
int sendto(int s, char *buf, int length, int flags,
sockaddr* to, int tolen);
int recvfrom(int s, char* buf, int length, int flags,
sockaddr* from, socklen_t *fromlen);
s // 자신의 소켓 기술자
buf // 송수신 버퍼
length // 버퍼의 크기
flags // 보통 0
to // 목적지의 소켓 주소 구조체
tolen // 목적지의 소켓 주소 구조체 크기
from // 발신자의 소켓 주소 구조체
fromlen // 발신자의 소켓 주소 구조체 크기
RaspberryPi 소켓프로그래밍 43
UDP 방식(계속)
* Linux UDP Server 소스
//=======================================// udpSever.c (Linux)// for Chatting//=======================================#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <netinet/in.h>
#define PORTNUM 0x5005#define BUFFSIZE 256
int main(void) {struct sockaddr_in serv_addr, cli_addr;int serv_fd, cli_fd, clilen;char buffer[BUFFSIZE];
printf("[UDP Server for Chatting...\n");
// 1) create server socket
RaspberryPi 소켓프로그래밍 44
if((serv_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {perror("ERROR opening socket");exit(1);
}
// 2) setting server socket structurememset((char *) &serv_addr, 0x00, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = INADDR_ANY;serv_addr.sin_port = htons(PORTNUM);
// 3) bind()if(bind(serv_fd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) == -1) { perror("ERROR on binding");exit(1);
}
while(1) {// 4) recvfrom/sendto, memset(buffer, 0x00, sizeof(buffer));clilen = sizeof(cli_addr);recvfrom(serv_fd, buffer, BUFFSIZE, 0,
(struct sockaddr *)&cli_addr, &clilen);printf("[Guest] %s\n", buffer);if(buffer[0] == 'q') {
close(cli_fd);break;
RaspberryPi 소켓프로그래밍 45
}
// 4) recvfrom/sendto, sending...printf("[Server] ");memset(buffer, 0x00, sizeof(buffer));fgets(buffer, BUFFSIZE, stdin);sendto(serv_fd, buffer, BUFFSIZE, 0,
(struct sockaddr *)&cli_addr, sizeof(cli_addr));if(buffer[0] == 'q')
break;}// 5) close(), close(serv_fd);
return 0;}
RaspberryPi 소켓프로그래밍 46
UDP 방식(계속)
* Linux UDP Client 소스
//=======================================// udpClient.c (Linux)// for Chatting//=======================================#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <netinet/in.h>
#define SERVERIP "192.168.0.40"#define PORTNUM 0x5005#define BUFFSIZE 256
int main(void) {struct sockaddr_in serv_addr;int cli_fd;int servlen;char buffer[BUFFSIZE];
RaspberryPi 소켓프로그래밍 47
printf("[UDP Client for Chatting...\n");
// 1) create socketif((cli_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){
perror("ERROR opening socket");exit(1);
}
serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(SERVERIP);serv_addr.sin_port = htons(PORTNUM);
while(1) {memset(buffer, 0x00, sizeof(buffer));printf("[Guest] ");fgets(buffer, BUFFSIZE, stdin);
// 2) recvfrom/sendto, sending....sendto(cli_fd, buffer, BUFFSIZE, 0,
(struct sockaddr *)&serv_addr, sizeof(serv_addr));if(buffer[0] == 'q')
break;
// 2) recvfrom/sendto, receiving....memset(buffer, 0x00, sizeof(buffer));servlen = sizeof(serv_addr);recvfrom(cli_fd, buffer, BUFFSIZE, 0,
(struct sockaddr *)&serv_addr, &servlen);
RaspberryPi 소켓프로그래밍 48
printf("[Server] %s\n", buffer);if(buffer[0] == 'q')
break;}
// 3) close(), disconnectionclose(cli_fd);
return 0;}
RaspberryPi 소켓프로그래밍 49
UDP 방식(계속)
[실습4] UDP 방식의 서버/클라이언트 I
: 소스 컴파일
$ make udpServer
$ make udpClient
: 리눅스 시스템간 동작을 확인
서버 측 $ ./udpServer
클라이언트 측 # ./udpClient
RaspberryPi 소켓프로그래밍 50
UDP 방식(계속)
* Windows UDP Client 소스
//=======================================// udpClientWin.c (Windows)// for Chatting//=======================================#include <stdio.h>#include <string.h>#include <stdlib.h>#include <winsock2.h>
//#define SERVERIP "192.168.0.40"#define PORTNUM 0x5005#define BUFFSIZE 256
int main(int argc, char *argv[]) {SOCKADDR_IN serv_addr;WSADATA wsaData;SOCKET cli_fd;int servlen;char buffer[BUFFSIZE];
if(argc != 2) {
RaspberryPi 소켓프로그래밍 51
printf("usage : %s [port]\n", argv[0]); exit(0);}
printf("[TCP client for chatting...]\n");
if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {perror("ERROR WSAStartup()");exit(1);
}
// 1) create socketif((cli_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){
perror("ERROR opening socket");exit(1);
}
serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // SERVERIPserv_addr.sin_port = htons(PORTNUM);
while(1) {memset(buffer, 0x00, sizeof(buffer));printf("[Guest] ");fgets(buffer, BUFFSIZE, stdin);
// 2) recvfrom/sendto, sending....sendto(cli_fd, buffer, BUFFSIZE, 0,
RaspberryPi 소켓프로그래밍 52
(struct sockaddr *)&serv_addr, sizeof(serv_addr));if(buffer[0] == 'q')
break;
// 2) recvfrom/sendto, receiving....memset(buffer, 0x00, sizeof(buffer));servlen = sizeof(serv_addr);recvfrom(cli_fd, buffer, BUFFSIZE, 0,
(struct sockaddr *)&serv_addr, &servlen);printf("[Server] %s\n", buffer);if(buffer[0] == 'q')
break;}
// 3) close(), disconnectionclosesocket(cli_fd);
return 0;}
: 컴파일 ... F7 펑션키를 이용
// 실행 파일 udpClientWin.exe이 생성
RaspberryPi 소켓프로그래밍 53
UDP 방식(계속)
[실습5] UDP 방식의 서버/클라이언트 II
: 2) 리눅스 서버와 Windows 클라이언트간 동작을 확인
서버 측 $ ./udpServer
클라이언트 측 c:\> udpClientWin 192.168.0.40
: 동작 결과
- 클라이언트 측에서 전송하는 문자열 중 첫 문자가 0 혹은 1에 따라
- 서버측 LED 디바이스를 OFF 혹은 ON으로 제어
RaspberryPi 소켓프로그래밍 54
UDP 방식(계속)
* Linux UDP Server 소스 ( LED 제어용 )
//=======================================// udpSever_01.c (Linux)// for Chatting + controlling LED//=======================================#include <stdio.h>#include <stdlib.h>
RaspberryPi 소켓프로그래밍 55
#include <string.h>#include <unistd.h>#include <netinet/in.h>
#include <wiringPi.h> //
#define PORTNUM 0x5005#define BUFFSIZE 256
#define P_LED 1 // BCM_GPIO #18
int main(void) {struct sockaddr_in serv_addr, cli_addr;int serv_fd, cli_fd, clilen;char buffer[BUFFSIZE];
printf("[UDP Server for Chatting and controlling LED...\n");
// 1) create server socketif((serv_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("ERROR opening socket");exit(1);
}
// 2) setting server socket structurememset((char *) &serv_addr, 0x00, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = INADDR_ANY;
RaspberryPi 소켓프로그래밍 56
serv_addr.sin_port = htons(PORTNUM);
// 3) bind()if(bind(serv_fd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) == -1) { perror("ERROR on binding");exit(1);
}
if(wiringPiSetup() == -1) // wiringPi pin #return 1;
pinMode(P_LED, OUTPUT); //
while(1) {// 4) recvfrom/sendto, memset(buffer, 0x00, sizeof(buffer));clilen = sizeof(cli_addr);recvfrom(serv_fd, buffer, BUFFSIZE, 0,
(struct sockaddr *)&cli_addr, &clilen);printf("[Guest] %s\n", buffer);if(buffer[0] == 'q') {
close(cli_fd);break;
}else if(buffer[0] == '0') // LED control....
digitalWrite(P_LED, LOW);else if(buffer[0] == '1')
RaspberryPi 소켓프로그래밍 57
digitalWrite(P_LED, HIGH);
// 4) recvfrom/sendto, sending...printf("[Server] ");memset(buffer, 0x00, sizeof(buffer));fgets(buffer, BUFFSIZE, stdin);sendto(serv_fd, buffer, BUFFSIZE, 0,
(struct sockaddr *)&cli_addr, sizeof(cli_addr));if(buffer[0] == 'q')
break;}// 5) close(), close(serv_fd);
return 0;}
: 소스 컴파일
$ make udpServer_01
RaspberryPi 소켓프로그래밍 58
UDP 방식(계속)
[실습6] UDP 서버/클라이언트에 의한 LED 원격 제어
: 클라이언트는 기존 Linux용, 혹은 Windows용 사용
: 메시지의 첫 문자를 0, 1에 따라 LED 제어
1) 리눅스 시스템간 동작을 확인
서버 측 $ ./udpServer_01
클라이언트 측 # ./udpClient
2) 리눅스 서버와 Windows 클라이언트간 동작을 확인
서버 측 $ ./udpServer_01
클라이언트 측 c:\> udpClientWin 192.168.0.40