LAB 01 - 소켓 프로그래밍을 이용한 파일 전송
요구사항
1. 동작내용
서버와 클라이언트 간의 1:1 통신으로 가정
전송하는 파일은 단순 텍스트 파일로 가정
클라이언트의 동작 디렉토리내 파일을 서버로 전송하는 것으로 가정. 전송할 파일 이름은 코드실행시 argument로 전달 (아래 참고).
클라이언트는 1) argument로 받은 파일이름을 갖고 파일 오픈하고 성공적이면 파일이름을 서버로 먼저 전송. 2)오픈된 파일을 읽어서 서버로 계속 데이터 전송
서버는 파일이름을 받아 동작디렉토리에 파일을 오픈 또는 생성을 수행. 성공적이면 클라이언트에서 전송하는 데이터를 읽어서 파일에 저장
파일 내용이 버퍼의 크기(예:1096)를 초과하는 경우를 대비하여 그럴 경우 반복문을 통해 여러 번 읽고 쓰도록 구현.
C언어 표준 파일 입출력함수를 사용(예: fopen()).
전송하는 파일은 내용이 들어 있는 정상 파일로 가정. (파일 전송하는 클라이언트 c 소스 파일을 사용해도 됨).
서버와 클라이언트가 같은 컴퓨터위에서 동작하나 본 과제를 위해서는 서버용 코드를 저장하는 디렉토리와 클라이언트용 코드를 저장하는 디렉토리를 다르게 생성하여 test 해야 함.
2. 코드 실행예제
socket-file-server.c 파일에 서버 소스코드 작성
$ gcc –o socket-file-server socket-file-server.c
$./socket-file-server 9190
socket-file-client.c 파일에 클라이언트 소스코드 작성
$ gcc –o socket-file-client socket-file-client.c
$./socket-file-client 127.0.0.1 9190 전송할파일이름
socket-file-server.c 소스코드
#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 serv_sock;
int clnt_sock;
char buf[256];
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[]="Hello World!";
if(argc != 2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
if(serv_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=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1) error_handling("bind() error");
if(listen(serv_sock, 5)==-1)
error_handling("listen() error");
clnt_addr_size=sizeof(clnt_addr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
if(clnt_sock==-1)
error_handling("accept() error");
write(clnt_sock, message, sizeof(message));
int nbyte = 256;
size_t filesize = 0, bufsize = 0;
FILE *file = NULL;
// 원래는 보내는 파일 이름으로 받아야 하는데.. 포기. 그냥 recieved file 이란 이름으로 받음.
// 일단 요구대로 표준 함수 fopen()사용.
file = fopen("recieved file", "wb");
bufsize = 256;
// 파일 내용이 버퍼의 크기(예:1096)를 초과하는 경우를 대비하여 그럴 경우 반복문을 통해 여러 번 읽고 쓰도록 구현.
while(nbyte != 0)
{
nbyte = recv(clnt_sock, buf, bufsize, 0);
fwrite(buf, sizeof(char), nbyte, file);
}
fclose(file);
close(clnt_sock);
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputs("\n", stderr);
exit(1);
}
socket-file-client.c 소스코드
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
void error_handling(char *message);
int main(int argc, char* argv[])
{
int serv_sock, fd;
int str_len, len;
struct sockaddr_in serv_addr;
char message[30], buf[BUFSIZ];
FILE *file = NULL;
// argument 4개로 제한. $ [실행파일] [ip addr] [port num] [file name]
if(argc != 4){
printf("Usage : %s <IP> <port> \n", argv[0]);
exit(1);
}
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
if(serv_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(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
error_handling("connect() error!");
str_len=read(serv_sock, message, sizeof(message)-1);
if(str_len==-1)
error_handling("read() error!");
size_t fsize, nsize = 0;
size_t fsize2;
// 3번째 argument를 파일명으로 인식함(argument는 0부터 count)
file = fopen(argv[3], "rb");
fseek(file, 0, SEEK_END);
fsize = ftell(file);
fseek(file, 0, SEEK_SET);
while (nsize != fsize) {
int fpsize = fread(buf, 1, 256, file);
nsize += fpsize;
send(serv_sock, buf, fpsize, 0);
}
fclose(file);
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
실행결과
이렇게 하면 Lab1/Lab1_server에 received file이란 이름으로 client에서 전송한 파일이 전송된다.
근데 이것저것 고치다보니 갑자기 안됨
일단 과제 제출 시점에는 됐으니까 상관없겠지..
원래 이런거 작성하는 블로그 따로 있는데 번거로워서 일단 서이로 여기다 저장해둔다
학기 끝나면 전체로 풀 예정
** 수정
껐다켜니까 됐음
++

문서 디렉토리에 있는 Lab1파일하고 홈 디렉토리에 있는 Lab1파일 이름을 똑같이 만들어서 파일 수신이 되고있지 않다고 착각한 듯.
다시 들어가보니 수신 파일이 여러개 생성되어 있었는데 보니까 파일 첫 부분을 먼저 받아오는 것까지는 성공했는데 포인터 배열로 파일 이름 참조해서 가져오는 건 실패..
일단 다른 시험공부부터 하고 이건 시간될 때 더 붙잡아보기로 결정