일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 국토교통부
- 월세
- 프리미어 영상변환
- #신혼부부 #결혼준비 #신혼부부희망타운신혼부부특별공급
- object
- 프리미어 영상저장
- file write
- FILE TRANSFER
- 엑스퍼트생일축하해
- #부동산전자거래 #부동산전자계약 #부동산계약 #부동산전자계약방법 #부동산전자계약하는법 #부동산계약방법 #부동산중개수수료 #부동산중개수수료아끼기 #부동산복비아끼기
- TCP/IP
- QTcpServer
- 중소규모택지
- lua setup
- 등록임대주택
- 찾다죽는줄
- file open
- lua install
- C API
- Lua
- C++ API
- QT TCP
- lua interpreter
- 티몬삼겹살데이
- lua for windows
- 엑스퍼트2주년
- 청량리역한양수자인192
- file read
- meta table
- 수도권주택공급
- Today
- Total
Value Creator의 IT(프로그래밍 / 전자제품)
Chapter 16 입출력 스트림 분리 본문
앞서 설명한 입출력 스트림 분리는 2가지이다.
2019/10/30 - [1. 프로그래밍/4) Network] - Chapter 10 멀티 프로세스 기반의 서버 구현
--> 멀티 프로세스 기반 입출력 분리
2019/11/07 - [1. 프로그래밍/4) Network] - Chapter 15 소켓과 표준 입출력
--> FILE 구조체 포인터 기반 입출력 분리
입력과 출력을 분리해서 구현이 편리해지고, 버퍼링 기능이 향상된다(속도가 빨라진다).
멀티프로세스 기반의 입출력 분리에서는 Half-Close를 이용한 EOF 전달이 가능하다.
하지만 FILE 구조체 포인터 기반 입출력 분리에서는 기존의 Half-Close방법을 사용할 수 없다.
아래는 fdopen을 이용한 서버-클라이언트 프로그램에서 Half-Close가 진행되지 않는 예제이다.
sep_serv.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
FILE * readfp;
FILE * writefp;
struct sockaddr_in serv_adr, clnt_adr;
socklen_t clnt_adr_sz;
char buf[BUF_SIZE]={0,};
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));
bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr));
listen(serv_sock, 5);
clnt_adr_sz=sizeof(clnt_adr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr,&clnt_adr_sz);
readfp=fdopen(clnt_sock, "r"); //read와 write를 각각 구분하였다.
writefp=fdopen(clnt_sock, "w");
fputs("FROM SERVER: Hi~ client? \n", writefp); //클라이언트로 문자열을 전송한다.
fputs("I love all of the world \n", writefp); //클라이언트로 문자열을 전송한다.
fputs("You are awesome! \n", writefp); //클라이언트로 문자열을 전송한다.
fflush(writefp); //문자열 전송을 마무리한다.
fclose(writefp); //write 쪽만 먼저 닫았다. 상대방쪽으로 EOF를 전달한다.
fgets(buf, sizeof(buf), readfp);
fputs(buf, stdout);
fclose(readfp); //fgets와 fputs를 진행한 후에 read쪽을 닫았다.
return 0;
}
sep_clnt.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
int main(int argc, char *argv[])
{
int sock;
char buf[BUF_SIZE];
struct sockaddr_in serv_addr;
FILE * readfp;
FILE * writefp;
sock=socket(PF_INET, SOCK_STREAM, 0);
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]));
connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
readfp=fdopen(sock, "r");
writefp=fdopen(sock, "w");
while(1)
{
if(fgets(buf, sizeof(buf), readfp)==NULL)
break;
fputs(buf, stdout); //입력 버퍼에 있는 문자열을 출력한다.
fflush(stdout);
}
fputs("FROM CLIENT: Thank you! \n", writefp); //EOF를 수신한 뒤 서버쪽으로 문자열을 전송한다.
fflush(writefp); //flush를 명령해서 writefp 버퍼의 문자열 전송을 마무리한다.
fclose(writefp); //writefp를 닫는다.
fclose(readfp); //readfp를 닫는다.
return 0;
}
[서버]
a@a:~/Desktop/NP/manual/TCPIP_Src/Chapter16$ ./sepserv 9190
[클라이언트]
a@a:~/Desktop/NP/manual/TCPIP_Src/Chapter16$ ./sepclnt 127.0.0.1 9190
FROM SERVER: Hi~ client?
I love all of the world
You are awesome!
서버 쪽으로 FROM CLIENT : Thank you! 라는 명령어가 전송되었어야 하는데, 전송되지 않았다.
fclose(writefp) 때문에 서버 쪽의 소켓이 닫혔기 때문이다.
기존 : 하나의 파일 디스크립터를 이용해 읽기모드와 쓰기모드의 FILE 포인터를 만든다.
개선 : 파일디스크립터를 복사해서 두개로 만든 후 읽기모드용 파일 디스크립터와 쓰기모드용 파일 디스크립터로 분리한다.
dup.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int cfd1, cfd2;
char str1[]="Hi~ \n";
char str2[]="It's nice day~ \n";
cfd1=dup(1); //파일 디스크립터 1(표준 출력)을 cfd1로 복사한다.
cfd2=dup2(cfd1, 7); //cfd1의 파일디스크립터 속성을 cfd2로 복사한다. cfd2의 파일디스크립터 번호는 7로 지정한다.
printf("fd1=%d, fd2=%d \n", cfd1, cfd2); //파일 디스크립터 번호는 각각 3과 7이다.
write(cfd1, str1, sizeof(str1)); //str1을 cfd1에 전송한다. cfd1은 표준 출력이므로 str1이 화면에 표시된다.
write(cfd2, str2, sizeof(str2)); //str2를 cfd2에 전송한다. cfd2도 표준 출력이므로 str2가 화면에 표시된다.
close(cfd1);
close(cfd2);
write(1, str1, sizeof(str1)); //복사된 cfd1과 cfd2를 닫아도 기존의 파일디스크립터 1은 남아있다. 따라서 str1이 화면에 표시된다.
close(1);
write(1, str2, sizeof(str2)); //표준출력이 닫혔으므로 str2는 표시되지 않는다.
return 0;
}
이제 dup과 dup2 함수를 이용하여 서버의 소스코드를 수정하면 된다.
서버의 writefp를 닫아도, readfp는 살아있는 형태이다(전송은 닫히고, 수신만 가능한 상태)
sep_serv2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
FILE * readfp;
FILE * writefp;
struct sockaddr_in serv_adr, clnt_adr;
socklen_t clnt_adr_sz;
char buf[BUF_SIZE]={0,};
serv_sock=socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family=AF_INET;
serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_adr.sin_port=htons(atoi(argv[1]));
bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr));
listen(serv_sock, 5);
clnt_adr_sz=sizeof(clnt_adr);
clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr,&clnt_adr_sz);
readfp=fdopen(clnt_sock, "r");
writefp=fdopen(dup(clnt_sock), "w"); //dup 기능을 이용해서 clnt_sock 파일디스크립터를 복사했다.
fputs("FROM SERVER: Hi~ client? \n", writefp);
fputs("I love all of the world \n", writefp);
fputs("You are awesome! \n", writefp);
fflush(writefp);
shutdown(fileno(writefp), SHUT_WR); //shutdown을 이용해서 writefp로 EOF 신호를 보냈다.
fclose(writefp);
fgets(buf, sizeof(buf), readfp); fputs(buf, stdout); //writefp는 닫혔어도, readfp는 살아있음을 알 수 있다.
fclose(readfp);
return 0;
}
[서버]
a@a:~/Desktop/NP/manual/TCPIP_Src/Chapter16$ ./sepserv2 9190
FROM CLIENT: Thank you!
[클라이언트]
a@a:~/Desktop/NP/manual/TCPIP_Src/Chapter16$ ./sepclnt 127.0.0.1 9190
FROM SERVER: Hi~ client?
I love all of the world
You are awesome!
'1. 프로그래밍 > 4) Network' 카테고리의 다른 글
Chapter 18 멀티 쓰레드 기반의 서버 구현 (0) | 2019.11.11 |
---|---|
Chapter 17 epoll 기반 IO 멀티플렉싱 서버 구현 (0) | 2019.11.07 |
Chapter 15 소켓과 표준 입출력 (0) | 2019.11.07 |
Chapter 14 추후 작성 (0) | 2019.11.07 |
Chapter 13 입출력 함수(send, recv, read, write) (0) | 2019.11.06 |