라즈베리파이를 이용하여 카메라로 촬영한 이미지를 소캣통신으로 서버컴퓨터에 전달하려고 한다.
라즈베리파이는 라즈베리파이4를 이용하였고 라즈베리파이용 카메라는 야간에도 촬영이 가능한 적외선 카메라를 이용하였다.
터미널에서 raspistill 명령어를 이용해서 카메라를 제어합니다. 예를들어 1초후에 촬영하여 test.jpg라는 파일이름으로 저장하고 싶다면
raspistill -t 1000 -o test.jpg
라고 터미널에서 입력해 주면 사지촬영을 시작합니다. (단 , 경로는 터미널 현재 경로에 저장됩니다.)
이제 라즈베리파이랑 서버컴퓨터와 소캣통신을 해야합니다. 저는 구글링을 하면서 많이 찾아 다녔습니다. 이미지를 소캣통신을 하기위해 파일입출력을 이용하여 이미지파일을 읽어 데이터크기만큼 반복적으로 서버컴퓨터에 보냅니다. 아래 코드는 제가 구글에 검색하여 얻은 코드를 토대로 조금 수정하였습니다. ( 클라이언트 코드입니다.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUFSIZE 4096
int main()
{
char message[30];
int sock;
struct sockaddr_in serv_addr;
sock=socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1)
printf("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr("목적지 ip주소");
serv_addr.sin_port=htons(포트번호);
if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
printf("connect() error!");
recv(sock,message,sizeof(message),0);
char myFile[256] = "test.jpg";
while(1){
system("raspistill -t 700 -o test.jpg");
FILE *fp = fopen(myFile, "rb");
char filename[256];
memset(filename ,0, 256);
sprintf(filename , myFile);
send(sock,filename,256,0);
fseek(fp,0,SEEK_END);
int totalbytes = ftell(fp);
send(sock,(char*)&totalbytes, sizeof(totalbytes),0);
char buf[BUFSIZE];
int numread;
int numtotal =0;
rewind(fp);
while(1){
numread = fread(buf,1,BUFSIZE,fp);
if(numread > 0) {
send(sock,buf,numread,0);
numtotal += numread;
}
else if(numread == 0 && numtotal == totalbytes){
printf("총 %d 바이트 파일 전송을 완료했습니다.\n",numtotal);
break;
}
}
fclose(fp);
sleep(8);
}
close(sock);
return 0;
}
보내고자 하는 파일이름을 먼저 서버에 보내줍니다. 그리고 파일의 크기를 읽어 서버에 보내줍니다. 마지막으로는 파일을 버퍼사이즈만큼 읽어 서버에 반복적으로 보내줍니다. 파일이 다 보내질때까지 반복합니다. 이렇게 이미지 파일을 서버에 보낼 수 있었습니다.
#include <stdlib.h>
#include <iostream>
#include <stdio.h>
#include <WinSock2.h>
#include <cstdlib>
// 버퍼 크기 정의
#define BUFSIZE 4096
// 경고 제어
#pragma warning(disable:4996)
// 윈속 사용을 위한 라이브러리 링킹
#pragma comment(lib, "Ws2_32.lib")
// 메인 함수
int main() {
int count = 0; // 숫자세기
// 데이터 크기를 담을 변수
int retval;
char message[30] = "사진을 보내도 됩니다.";
char commend;
// 윈속 객체 선언 및 초기화
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
// 연결용 소켓을 생성
SOCKET listen_sock = socket(AF_INET, SOCK_STREAM, 0);
// 바인딩 처리 변수 초기화
SOCKADDR_IN servaddr;
ZeroMemory(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(포트번호);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 루프백 아이피
// 바인딩
bind(listen_sock, (SOCKADDR*)&servaddr, sizeof(servaddr));
// 연결 함수 시작
listen(listen_sock, SOMAXCONN);
// 데이터 통신에 사용할 변수
SOCKET client_sock;
SOCKADDR_IN clientaddr;
int addrlen;
char buf[BUFSIZE];
// 클라이언트 접속을 받음
addrlen = sizeof(clientaddr);
client_sock = accept(listen_sock, (SOCKADDR*)&clientaddr, &addrlen);
// 접속한 클라이언트 정보 출력
printf("클라이언트 접속 : IP = %s, Port = %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
printf("사진을 받으시겠습니까? ( Y or N ) :");
scanf_s(" %c", &commend);
if (commend == 'y' || commend == 'Y') {
send(client_sock, message, sizeof(message), 0);
}
else {
goto exit;
}
// 반복적으로 클라이언트로부터 소켓 요청을 받음
while (1) {
// 파일 이름 받기
char filename[256];
ZeroMemory(filename, 256);
recv(client_sock, filename, 256, 0);
// 받을 파일 이름 출력
printf("받을 파일 이름 : %s\n", filename);
// 파일 크기 받기
int totalbytes;
recv(client_sock, (char*)&totalbytes, sizeof(totalbytes), 0);
// 받을 파일 크기 출력
printf("받을 파일 크기 : %d 바이트\n", totalbytes);
// 파일 열기
FILE* fp = fopen(filename, "wb");
// 파일 데이터 받기
int numtotal = 0;
while (1) {
if (numtotal == totalbytes) break;
retval = recv(client_sock, buf, BUFSIZE, 0);
// 더 받을 데이터가 없을 때
if (retval == 0) {
break;
}
else {
fwrite(buf, 1, retval, fp);
k
// 받은 데이터 크기만큼 변수에 더해줌
numtotal += retval;
}
}
fclose(fp);
// 전송 결과
if (numtotal == totalbytes) {
printf("파일을 성공적으로 받았습니다.\n");
}
else
printf("파일을 제대로 받지 못했습니다.\n");
}
exit:
// 해당 클라이언트 소켓을 폐기
closesocket(client_sock);
printf("클라이언트 종료 : IP = %s, Port = %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
// 서버 소켓을 폐기
closesocket(listen_sock);
// 윈속 종료
WSACleanup();
return 0;
}
위 코드는 서버측 코드입니다. 서버에서는 클라이언트에서 보내는 파일이름과 파일크기를 받고 이미지 파일을 받을 때는 클라이언트에서 반복적으로 보내주는 크기를 받아서 합하여줍니다.
파일을 성공적으로 받게되면 서버측 프로젝트 파일 안에 "test.jpg"라는 파일이 새로 생기게 됩니다.
여기서 발생하는 문제점은 파일을 연속적으로 받게되면 받는 이름이 같기때문에 덮어 씌어집니다. 그러면 이전에 받았던 파일은 사라지게 됩니다. 추후에 파일명을 가변적으로 만들어주어 받은 파일들을 저장할 수 있는 기능을 구현해 보아야 할 것 같습니다.