문제 2번 ) client에서 파일 이름을 서버에 보내면 서버에서 파일에 접근해 파일의 내용을 읽어서 다시 클라이언트에게 리턴해주어라. 또한, 서버에 파일을 쓰는 것도 구현하여라.
<구현 방법>
fcntl.h 라이브러리를 include하여서 파일 입출력을 수행할 수 있도록 만들었다.
만약 클라이언트가 rd를 입력하면 파일을 읽는 동작을 수행한다. fopen을 통해서 파일을 읽기 권한으로 오픈하고, 내용을 buf에 저장한 후 클라이언트에게 전송한다.
클라이언트가 wrt를 입력하면 파일을 쓰는 동작을 수행한다. fopen을 "w" 옵션으로 열면 파일이 생성된다. 그 후에 클라이언트에게서 입력받은 파일 내용을 생성한 파일에 쓰고 파일을 닫는다.
<서버 코드>
//sol2_server.c
#include <stdio.h> //표준 입출력 라이브러리
#include <stdlib.h> //문자열 관련 함수 사용 위한 라이브러리
#include <string.h> //memset 등 사용
#include <unistd.h> // exit,sockaddr_in 등 사용
#include <fcntl.h> // 파일 입출력 관련 라이브러리
#include <sys/socket.h> // 소켓 관련 라이브러리
#include <arpa/inet.h> //아파넷 관련 라이브러리
#define MAX 500
// 에러 발생 시 내용 출력하는 에러 핸들러
void error_handling(char * message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
void main(){
int ss, sa;
struct sockaddr_in ssa;
// ssa 구조체 초기화 후 통신타입, ip주소, 포트번호 지정
memset(&ssa, 0, sizeof(ssa));
ssa.sin_family = AF_INET;
ssa.sin_addr.s_addr = htonl(INADDR_ANY); // 어떤 주소이든 상관없다.
ssa.sin_port = htons(11234); // 포트번호 11234
// tcp 통신 소켓 생성
ss = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(ss == -1) // 소켓 생성 실패할 경우 에러 메세지 출력
error_handling("socket error");
//소켓과 서버 주소를 연결한다. 실패할 경우 에러 메세지 출력
if(bind(ss,(struct sockaddr *) &ssa, sizeof(ssa))==-1){
error_handling("bind error");
}
// 서버는 무한루프를 돌면서 클라이언트의 요청이 있을 때 마다 서비스해준다.
while(1){
// 소켓을 사용가능한 상태로 활성화. 소켓 대기열의 크기는 10.
if(listen(ss,10)==-1){
error_handling("listen error");
}
//클라이언트로부터 요청이 오면 연결을 수락한다.
sa = accept(ss,0,0);
if(sa ==-1){
error_handling("accept error");
}
//클라이언트에서 명령어를 수신받아 command 배열에 저장한다.
char command[7];
recv(sa, command, MAX, 0);
//rd를 입력했을 때, 파일 읽기 수행
if(!strcmp(command, "rd\n")){
char filename[20],buf[MAX];
FILE *file;
filename[0] ='\0'; //filename 초기화
memset( buf, 0, sizeof(buf)); //buf 초기화
//클라이언트 측에서 읽을 파일 이름을 filename에 전송받는다.
recv(sa,filename,20,0);
// 입력받은 파일을 읽기 권한으로 열기
file = fopen(filename,"r");
//만약 file 오픈 실패했다면 에러메세지 출력
if (file==NULL){
printf("파일 열기 실패\n");
exit(1);
}
// file의 내용을 buf에 저장한다.
fgets(buf,sizeof(buf),file);
//클라이언트에게 buf의 내용을 전달한다.
send(sa,buf,sizeof(buf),0);
fclose(file);//파일 닫기
}
//파일 쓰기 명령어 수행
if(!strcmp(command, "wrt\n")){
char filename[20],buf[MAX];
FILE *file;
filename[0] ='\0';//filename 배열 초기화
memset( buf, 0, sizeof(buf)); // buf 배열 초기화
recv(sa,filename,20,0); //클라이언트에서 쓰기 할 파일 명을 수신받는다.
//입력받은 파일 이름의 파일 생성, 만약 있으면 삭제 후 재생성
file = fopen(filename,"w");
// 파일 오픈 실패 시 에러메세지 출력
if (file==NULL){
printf("파일 열기 실패\n");
exit(1);
}
//클라이언트에서 입력할 파일 내용을 전송받아 buf에 저장한다.
recv(sa,buf,sizeof(buf),0);
//buf의 내용을 file에 쓴다.
fprintf(file,buf,sizeof(buf));
//file 닫기
fclose(file);
}
close(sa); //클라이언트와의 통신 소켓을 닫는다.
}
}
<클라이언트 코드>
#include <stdio.h> //표준 입출력 라이브러리
#include <string.h>// memset 등 사용
#include <unistd.h> // exit, sockaddr_in 등 사용
#include <sys/socket.h> // 소켓 관련 라이브러리
#include <arpa/inet.h> //아파넷 관련 라이브러리
#include <fcntl.h> //파일입출력 관련 라이브러리
#include <stdlib.h> // 문자열 관련 함수 사용 위한 라이브러리
#define MAX 500
#define IPADDR "127.0.0.1"
// 에러 발생 시 내용 출력하는 에러 핸들러
void error_handling(char * message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
void main(){
int cs;
char buf[MAX],command[5],filename[20],temp[5];
struct sockaddr_in csa;
//csa 구조체 초기화 후 통신타입, ip주소, 포트번호 지정
memset(&csa, 0, sizeof(csa));
csa.sin_family = AF_INET;
csa.sin_addr.s_addr = inet_addr(IPADDR); // 루프백 주소인 127.0.0.1 지정
csa.sin_port = htons(11234);// 포트번호 11234
// tcp 통신 소켓 생성
cs = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(cs ==-1){ // 소켓 생성 실패할 경우 에러 메세지 출력
error_handling("client socket error");
}
// 서버와 소켓 연결한다. 연결 실패 시 에러 메세지 출력
if(connect(cs, (struct sockaddr *) &csa, sizeof(csa))==-1){
error_handling("connect error");
}
//파일을 read할 지, write 할 지 입력을 받아 command 문자열에 저장한다.
printf("\n <명령어 Read File :rd, Write File: wrt>\n");
printf("[command] ");
fgets(command,5,stdin);
send(cs,command,5,0); //명령어를 서버에 전송한다.
//파일 읽기 수행
if(!strcmp(command,"rd\n")){
//읽고싶은 파일 명을 입력하여 서버에게 전송한다
printf ("읽고싶은 파일 명 : ");
scanf("%s",filename);
fgets(temp,5,stdin); // 버퍼에 남은 \n 제거
send(cs,filename,20,0);
//buf 배열 초기화
memset( buf, 0, sizeof(buf));
// 서버가 전송하는 파일의 내용을 받아와 buf에 저장한다
recv(cs,buf,sizeof(buf),0);
// 파일의 내용을 출력한다.
printf("Open file '%s' : [ %s ]\n", filename, buf);
}
//파일 쓰기 명령어 수행
else if(!strcmp(command,"wrt\n")){
//buf 배열 초기화
memset( buf, 0, sizeof(buf));
//생성할 파일 명을 입력받는다.
printf ("생성할 파일 명 : ");
scanf("%s",filename); //문자열을 입력받아 filename에 저장한다.
fgets(temp,5,stdin);//버퍼에 남은 \n 제거
send(cs,filename,20,0); // 서버에게 파일 이름을 전송한다.
//파일에 저장할 내용을 입력받는다.
printf("파일에 저장할 내용 :");
fgets(buf,MAX,stdin); // buf에 내용을 입력받는다.
send(cs,buf,sizeof(buf),0); //서버에게 파일에 저장할 내용을 전송한다.
printf("파일 생성이 완료되었습니다.\n");
<실행결과>
원래 있던 a.txt를 rd 명령어를 수행하여 화면에 보였다. cat a.txt로 확인한 파일 내용과 동일한 것을 확인할 수 있다.
wrt 명령어를 수행하여 b.txt 를 생성하였고, 다시 rd 명령어를 수행하여 화면에 띄워보였다. cat b.txt로 확인한 파일 내용과 동일한 것을 확인할 수 있다.
'CS지식 > 컴퓨터네트워크' 카테고리의 다른 글
네트워크 계층에 대한 정리 (계층 별 Switch 정리) (0) | 2023.08.08 |
---|---|
소켓 프로그래밍 과제 3번 (0) | 2023.05.23 |
소켓 프로그래밍 과제 1번 (0) | 2023.05.13 |
Transport Layer(전송 계층) _ TCP (1) | 2023.04.30 |
Transport Layer(전송계층), UDP (0) | 2023.04.28 |