관리 메뉴

Value Creator의 IT(프로그래밍 / 전자제품)

Chapter 15 소켓과 표준 입출력 본문

1. 프로그래밍/4) Network

Chapter 15 소켓과 표준 입출력

valuecreatort 2019. 11. 7. 19:15
반응형

 

 

-----------------------------------------------------------------------------------------------------------------------------------

 

표준 입출력 함수는 버퍼링을 사용하기 때문에 전송속도가 빠르다.

같은 경로에 임의의 파일을 하나 만들어놓고, 복사속도를 비교해보면 알 수 있다.

용량이 클수록 속도 차이를 확연히 느낄 수 있다.

 

시스템 함수(open, close, read, write)

syscpy.c

#include <stdio.h>
#include <fcntl.h>
#define BUF_SIZE 3

int main(int argc, char *argv[])
{
	int fd1, fd2, len;
	char buf[BUF_SIZE];

	fd1=open("test300mb.zip", O_RDONLY);
	fd2=open("cpy_test300mb.zip", O_WRONLY|O_CREAT|O_TRUNC);

	while((len=read(fd1, buf, sizeof(buf)))>0)
		write(fd2, buf, len);

	close(fd1);
	close(fd2);
	return 0;
}

 

 

 

 

표준 입출력 함수(fopen, fclose, fgets, fputs)

stdcpy.c

#include <stdio.h>
#define BUF_SIZE 3

int main(int argc, char *argv[])
{
	FILE * fp1; //표준 입출력함수는 FILE 구조체 포인터를 사용한다.
	FILE * fp2;
	char buf[BUF_SIZE];
	
	fp1=fopen("test300mb.zip", "r");
	fp2=fopen("cpyStdcpy_test300mb.zip", "w");

	while(fgets(buf, BUF_SIZE, fp1)!=NULL)
		fputs(buf, fp2); 

	fclose(fp1);
	fclose(fp2);
	return 0;
}

 

소켓을 생성할 때 반환되는 것은 파일 디스크립터이다. 파일 디스크립터를 FILE 구조체 포인터로 변환해야 표준 입출력 함수에서 소켓을 사용할 수 있다.

 

 

 

fdopen 함수를 이용. 파일 디스크립터를 FILE 구조체 포인터로 변환.

(file descriptor -> FILE 구조체 포인터)

desto.c

#include <stdio.h>
#include <fcntl.h>

int main(void)
{
	FILE *fp; 
	int fd=open("data.dat", O_WRONLY|O_CREAT|O_TRUNC); //파일 디스크립터 선언 
	if(fd==-1)
	{
		fputs("file open error", stdout);
		return -1;
	}

	fp=fdopen(fd, "w"); //write 모드의 FILE 구조체 포인터 반환 
	fputs("Network C programming \n", fp); 
	fclose(fp);
	return 0;
}

 

 

 

fileno 함수를 이용. FILE 구조체 포인터를 파일 디스크립터로 변환.

(FILE 구조체 포인터 -> 파일 디스크립터)

todes.c

#include <stdio.h>
#include <fcntl.h>

int main(void)
{
	FILE *fp;
	int fd=open("data.dat", O_WRONLY|O_CREAT|O_TRUNC); //파일 디스크립터 선언 
	if(fd==-1)
	{
		fputs("file open error", stdout);
		return -1;
	}
	
	printf("First file descriptor: %d \n", fd);  //파일 디스크립터 번호 출력 
	fp=fdopen(fd, "w"); //파일 디스크립터를 FILE 구조체 포인터로 변환 
	fputs("TCP/IP SOCKET PROGRAMMING \n", fp); 
  	printf("Second file descriptor: %d \n", fileno(fp)); //FILE 구조체 포인터를 파일 디스크립터로 변환 
	fclose(fp);
	return 0;
}

 

 

 

 

아래 코드는 Chapter4의 에코 서버와 에코 클라이언트 소스코드를 약간 변경한 것이다.

fdopen함수와 표준 입출력 함수를 사용한다는 것만 변했다.

2019/10/29 - [1. 프로그래밍/4) Network] - Chapter 4 TCP 기반 서버 클라이언트

 

소켓 기반 표준 C 입출력 함수 호출

echo_stdserv.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
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int serv_sock, clnt_sock;
	char message[BUF_SIZE];
	int str_len, i;
	
	struct sockaddr_in serv_adr;
	struct sockaddr_in clnt_adr;
	socklen_t clnt_adr_sz;
	FILE * readfp; //입력을 받을 소켓을 FILE 구조체 포인터로 선언
	FILE * writefp; //출력을 받을 소켓을 FILE 구조체 포인터로 선언 
	
	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_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]));

	if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
		error_handling("bind() error");
	
	if(listen(serv_sock, 5)==-1)
		error_handling("listen() error");
	
	clnt_adr_sz=sizeof(clnt_adr);

	for(i=0; i<5; i++)
	{
		clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
		if(clnt_sock==-1)
			error_handling("accept() error");
		else
			printf("Connected client %d \n", i+1);
	
		readfp=fdopen(clnt_sock, "r"); //fdopen으로 클라이언트 소켓 파일디스크립터의 입력부분을 FILE 구조체 포인터로 변환  
		writefp=fdopen(clnt_sock, "w"); //클라이언트 소켓 파일디스크립터의 출력부분을 FILE 구조체 포인터로 변환 
	
		while(!feof(readfp)) //EOF가 들어올때까지 읽기 실행 
		{
			fgets(message, BUF_SIZE, readfp);
			fputs(message, writefp);
			fflush(writefp); //fflush를 통해서 데이터 전송?
		}
		fclose(readfp);
		fclose(writefp);
	}
	close(serv_sock);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

 

 

 

echo_stdclnt.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
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int sock;
	char message[BUF_SIZE];
	int str_len;
	struct sockaddr_in serv_adr;
	FILE * readfp;
	FILE * writefp;

	if(argc!=3) {
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}
	
	sock=socket(PF_INET, SOCK_STREAM, 0);   
	if(sock==-1)
		error_handling("socket() error");
	
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family=AF_INET;
	serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
	serv_adr.sin_port=htons(atoi(argv[2]));
	
	if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
		error_handling("connect() error!");
	else
		puts("Connected...........");

	readfp=fdopen(sock, "r");
	writefp=fdopen(sock, "w");	//각종 선언은 서버 부분과 유사 

	while(1) 
	{
		fputs("Input message(Q to quit): ", stdout); 
		fgets(message, BUF_SIZE, stdin); //메세지를 입력 받기 scanf와 유사 
		if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
			break; //입력받은 값이 q나 Q라면 while문 중단

		fputs(message, writefp); //message를 writefp라는 출력 버퍼로 옮김  
		fflush(writefp); //?? 
 		fgets(message, BUF_SIZE, readfp); //message를 readfp로 읽기?? 
		printf("Message from server: %s", message);
	}	
	fclose(writefp);
	fclose(readfp);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
반응형
Comments