PracticalSocketC_2nd_5장 - 구버젼

Download Report

Transcript PracticalSocketC_2nd_5장 - 구버젼

얇지만 얇지 않은
TCP/IP 소켓 프로그래밍 C 2판
(TCP/IP Sockets in C 2/e, Morgan Kaufmann)
마이클 도나후(Michael J. Donahoo)
케네스 칼버트(Kenneth L. Calvert)
Chapter 05 Sending and Receiving Data
제 5장 데이터의 송수신
• 5.1 정수 인코딩
• 5.2 메시지 생성, 프레이밍, 그리고 파싱
• 5.3 마무리
2
TCP/IP 바이트 전송
• TCP/IP 프로토콜은 바이트(bytes)를 전송함
• 응용 프로그램끼리 의미를 부여할 뿐, TCP/IP는 단지 단
위 데이터를 전송
– 택배 기사는 내용물에 큰 의미를 부여하지 않으며 단지 택배 박
스를 원하는 곳으로 전달할 뿐
Application
byte stream
TCP/IP
Application
Here are some
bytes. I don’t
know what
they mean.
byte stream
TCP/IP
I’ll pass
these to
the app.
It knows
what to do.
3
전송 데이터의 자료형
• 문자열(String) 전송: 가변 길이 전송
– 장점
• 사람이 읽기 쉬움
• 메시지의 확장이 용이하며 무제한
– 단점
• 전송량 대비 전송 내용 비효율, 수신 루틴 비효율, 연산 비효율
– 상호 협의 할 내용
• 문자 코드 페이지
–
ASCII, Unicode, UTF
• 메시지 경계 구분 (프레이밍: framing)
–
–
길이 명시 방식: 전송할 문자열의 크기를 고정크기의 자료형에 담아서 전송. 수신자는 크기를
미리 파악하고 정확한 문자열만큼 수신
구분자 방식: 널 문자 혹은 임의의 문자를 메시지의 경계에 삽입. 수신자는 바이트 단위로 읽다
가 구분자가 나오면 메시지의 끝으로 확인
4
전송 데이터의 자료형
• 문자열(String) 전송
– 숫자 전송의 예
49
‘1’
55
57
‘7’
‘9’
57
‘9’
56
55
‘8’
‘7’
48
10
‘0’
\n
– 문자 전송의 예
0
3
77
0
111
0
109
M
o
m
77
111
109
0
10
\n
5
문자열 전송의 예(TCP)
char string[strBuffSize];
send(sock, string, strBuffSize, 0)
• 주의점 : 버퍼의 크기
– TCP는 운영체제에 의존적인 TCP 버퍼가 있으며 이 보다 작은
크기로 send()를 호출해야 한다.
6
전송 데이터의 자료형
• 정수형(Integer)의 전송
– 기본 자료형의 단위로 전송
• 2바이트, 4바이트 단위의 전송
– 주의 사항
• 2바이트 이상의 데이터 전송간에는 항상 네트워크 바이트 순서로 전송해야
함
• 1바이트 교환은 의미가 없음
• Network byte order (Big-Endian)
- 다중 바이트의 메시지 교환에 필수
- 호스트 바이트-네트워크 바이트 변환 함수들
• htonl(), htons(), ntohl(), ntohs()
Little-Endian
0
0
92
246
23,798
Big-Endian
246
92
0
0
7
정수 자료형 전송의 예(TCP)
int data;
send(sock, &data, sizeof(data), 0)
• ‘short’, ‘long’, ‘double’, ‘char’도 동일한 방식으로 처리
8
메시지 구성(Message Composition)
• 메시지 구성이란?
– 응용 프로그램끼리 의미 있는 데이터 교환을 위한 형식 정의
– 응용 프로토콜과 같은 의미
• 일반적으로 프로토콜 헤더+데이터로 구성
– 프로토콜(메시지) 필드란?
• 메시지 내부에서 단위 의미를 가지는 데이터 영역
• 예)파일 전송을 위한 메시지 구성
– 파일을 상대방에게 전송하기 위해서는 파일 내용도 중요하지만
파일 이름, 크기등과 같은 파일특성도 전달이 되어야 함
– 파일 전송을 위한 프로토콜 헤더 구성
• 아래와 같은 3개의 필드로 구성이 가능(정의에 따라 고정 혹은 가변 크기)
–
파일 이름, 파일 크기, 파일 퍼미션
– 파일 전송을 위한 프로토콜 데이터 구성
• 파일의 내용(항상 가변)
9
메시지 구성(Message Composition)
• 고정길이 필드
– 정수형 자료형으로 구성
• 메시지 처리가 쉬우며 일반적으로 크기가 작음
• 가변길이 필드
– 문자열로 구성
• Human-readable하며 확장성이 좋으나 처리가 불편하고 크기가 클 수 있음
• 고정길이 필드
integer
short
short
• 가변길이 필드
M
i
k
e
1
2
\n
10
메시지 구성을 위한 자료형 정의
• 구조체 사용이 바람직함
struct file_header {
int fileSize;
char fileName[256];
} sndFileHdr;
send(sock, &sndFileHdr, sizeof(sndFileHdr), 0)
• 저지르기 쉬운 실수
– 구조체 내부의 포인터 변수
• 값을 전달해야지 위치를 전달하는 것은 무의미
– 패딩, 정렬 문제
• 항상은 아니지만 가끔 일어나며 디버깅 어렵게 함
11
채우기(padding)의 문제
struct tst {
short x;
int y;
short z
};
x
[pad]
y
z
•
시스템은 내부 정렬 규칙에 따라 채우기를 수행
•
해결 방법
–
–
[pad]
구조체 멤버를 재 배치
명시적인 강제 채우기
12
파일 전송 메시지 구성 예제(고정 크기 메시지)
• 전송 방식: 고정 크기(바이너리 전송)
• 필요한 전송 정보
– 파일 이름(최대 255자 => 255byte의 메모리 공간 필요)
– 파일 크기(4byte의 경우 최대 4GB 크기의 파일 처리 가능)
– 파일 내용(가변 길이, 0~4GB 크기)
• 메시지 구성
FileName (255bytes)
FileSize (4bytes)
메시지 헤더
Filecontents (0~4GB)
메시지 데이터
struct msghdr {
char filename[255];
unsigned int filesize;
}
13
파일 전송 메시지 구성 예제(문자열 메시지 )
• 전송 방식 : 가변 길이(문자열 전송 방식)
• 필요한 전송 정보
– 구분자 ( !$ : 임의로 결정, 단 파일 이름이나 길이에 나오지 않아
야함)
– 파일 이름(크기 제한 없음, 필드의 크기는 파일 이름에 따라 가변
)
– 파일 크기(크기 제한 없음, 필드의 크기는 파일 크기에 따라 가변
)
– 파일 내용(가변 길이, 0~4GB 크기)
• 메시지 구성(255bytes의 helloworld.c를 전송할 경우)
Helloworld.c!$255!$
메시지 헤더
Filecontents (0~4GB)
메시지 데이터
14
파일 내용의 전송
• 파일 크기가 소켓 버퍼의 크기보다 크므로 아래와 같이 순
차적 전송
#define BUFSIZE 1024
char fileBuf[BUFSIZE];
fp = fopen(“test.txt", "r");
if(fp == NULL)
DieWithError ("File open error");
while(1){
len=fread(fileBuf, sizeof(char), BUFSIZE, fp);
send(sock, fileBuf, len, 0);
if(feof(fp))
break;
}
15
파일 내용의 수신
#define BUFSIZE 1024
char fileBuf[BUFSIZE];
recvFileSize=0;
fp = fopen(“test.txt", “w");
if(fp == NULL)
DieWithError ("File open error");
while(origFileSize>recvFileSize) {
if ((recvMsgSize = recv(clntSock,fileBuf,BUFSIZE, 0)) < 0)
DieWithError("recv() failed");
recvFileSize+=recvMsgSize;
fwrite(fileBuf, sizeof(char), BUFSIZE, fp);
}
16
과제
• 기존 에코 프로그램을 수정하여 다음의 기능을 가지는 프
로그램을 작성하라
– 클라이언트: 클라이언트 명령어의 두 번째 인자를 에코 문자열
대신 파일 이름으로 받아들여 파일을 서버에 전송하라
• Ex) FileClient 203.252.164.144 test.txt 5000
– 서버: 해당 파일은 서버의 실행파일이 존재하는 디렉토리에 동일
한 이름으로 저장되도록 한다.
• 심화 과제
– 전송 중간에 중단되어도 이어 보내기가 가능하도록 수정하라
17
좀 더 복잡한 메시지(응용 프로토콜)
의 구성
메시지 구성
• 파일 전송의 예
char FileName[256];
int FileSize;
char FileBuffer[1024];
FileName
FileSize
File Contents
Server
Client
recv(clntSock, FileName, 256, 0)
send(sock, FileName, 256, 0)
recv(clntSock, FileSize, 4, 0)
send(sock, FileSize, 4, 0)
while(notRecvfullFile()) {
while(feof(fd)) {
Send(sock, FileBuffer, 1024,0)
recv(sock, FileBuffer, 1024,0)
}
}
19
에코와 파일 전송을 모두 지원하는 프로토콜
• 상황
– 클라이언트는 서버에게 string 혹은 파일을 업로드 할 수 있다.
– 서버는 string을 받을 경우 echo를 해주고 파일을 받을 경우, 디
스크에 저장을 한 후 잘 받았다는 메시지를(acknowledge) 회신
한다
– 클라이언트는 echo메시지를 수신한 경우 ,echo 메시지를 출력
하고, file ack를 받을 경우 file ack를 출력한다
• 예
– client 203.252.164.144 upload test.txt 5000 // 파일 전송
– client 203.252.164.144 echo hello 5000
// 에코 메시지
20
메시지(프로토콜) 설계
• 서버와 클라이언트는 동일 프로그램으로 두 개의 다른 상
황을 모두 만족해야 함
– EchoString
string
Server
Client
Echostring
– File Upload
FileName
FileSize
Server
File Contents
Client
MsgType
• 서버의 입장에서 클라이언트의 서비스 요청이 에코요청인
지 파일업로드인지 구분할 수 있는 방법은?
– 서비스 타입(에코 요청, 파일업로드) 필드를 준비하고 클라이언
트를 이를 통해 서버에게 서비스 종류를 알림
21
바이너리 프로토콜 설계
/* Message Type */
#define EchoReq
#define FileUpReq
#define EchoRep
#define FileAck
char MsgType;
– EchoString
MsgType
01
02
11
12
string
Server
Client
Echostring
MsgType
– File Upload
MsgType
FileName
FileSize
File Contents
Server
Client
MsgType
22
클라이언트 핵심 코드(바이너리 프로토콜)
/* Message Type */
#define
EchoReq
01
#define
FileUpReq 02
#define
EchoRep
11
#define
FileAck
12
char MsgType;
char * operation; //client 203.252.164.144 upload test.txt 5000
…
operation=argv[2]
…
If (!strcmp(operation,”upload”))
MsgType=FileUpReq
send(sock, &MsgType, 1,0)
…
MsgType
FileName
FileSize
else if (!strcmp(operation,”echo”))
MsgType=EchoReq
send(sock, &MsgType,1,0)
…
MsgType
string
else {
File Contents
fprintf(stderr, "Usage: %s <Server IP> <operation><operand> <Echo Port>\n",
argv[0]);
exit(1);
}
23
서버 핵심 코드(바이너리 프로토콜)
/* Message Type */
#define
EchoReq
01
#define
FileUpReq 02
#define
EchoRep
11
#define
FileAck
12
char MsgType;
…
recv(clntSock, &MsgType,1,0)
if (MsgType==FileUpReq) {
recv(clntSock, FileName, …
…
MsgType=FileAck;
send(clntSock, &MsgType, 1,0);
}
else if(MsgType==EchoReq) {
recv(clntSock, EchoString, …
MsgType=EchoRep;
send(clntSock, EchoString,…);
}
else fprintf(stderr, “Bad request”)
MsgType
MsgType
FileName
FileSize
File Contents
string
24
문자열 프로토콜 설계
/* Message Type */
#define EchoReq
#define FileUpReq
#define EchoRep
#define FileAck
char MsgType[10];
“EchoReq”
“FileUpReq”
“EchoRep”
“FileAck”
– EchoString
MsgType
string
Server
Client
Echostring
MsgType
– File Upload
MsgType
FileName
FileSize
File Contents
Server
Client
MsgType
25
클라이언트 핵심 코드(문자열 프로토콜)
/* Message Type */
#define EchoReq
#define FileUpReq
#define EchoRep
#define FileAck
#define Delimeter
“EchoReq|”
“FileUpReq|”
“EchoRep|”
“FileAck|”
‘|”
char msgType[10];
char * operation; //client 203.252.164.144 upload test.txt 5000
…
operation=argv[2]
…
If (!strcmp(operation,”upload”))
MsgType FileName
strcpy(msgType, FileUpReq);
send(sock, msgType, strlen(msgType),0)
…
else if (!strcmp(operation,”echo”))
strcpy(msgType, EchoReq);
MsgType
send(sock, msgType,strlen(msgType,0)
…
else {
FileSize
File Contents
string
fprintf(stderr, "Usage: %s <Server IP> <operation><operand> <Echo Port>\n",
argv[0]);
exit(1);
}
26
서버 핵심 코드(문자열 프로토콜)
/* Message Type */
#define EchoReq
“EchoReq|”
#define FileUpReq
“FileUpReq|”
#define EchoRep
“EchoRep|”
#define FileAck
“FileAck|”
#define Delimeter
‘|”
char msgType[10], recvChar; int i=0;
…
while(i<10) {
recvChar=recv(clntSock, msgType[i],sizeof(char),0);
if (msgType[i]==Delimeter) break;
}
if (!(strcmp(msgType,FileReq)) {
recv(clntSock, FileName, …
MsgType
…
strcpy(msgType,FileAck);
send(clntSock, msgType, strlen(msgType),0);
}
else if(!(strcmp(msgType, EchoReq)) {
MsgType
recv(clntSock, EchoString, …
strcpy(msgtype,EchoRep);;
send(clntSock, msgType, strlen(msgTytpe),0);
…
}
else fprintf(stderr, “Bad request”)
FileName
FileSize
File Contents
string
27
과제 – Simple FTP
• 개요
– 서버와 클라이언트는 파일을 주고 받는다
• 상세 정보
– 클라이언트는 실행 인자로 서버의 IP와 Port를 입력 받는다
• SFtpclient 203.252.164.144 5000
– 실행 후 표준 입력을 통해 클라이언트는 다음의 명령어를 입력
받을 수 있다
• get(download from server), put(upload to server), exit
– Up/download시 서버는 ack(수신성공 메시지)를 클라이언트에
게 전송하며 ack를 수신하면 클라이언트는 송수신 성공 메시지
를 화면에 출력한다
– 프로토콜 헤더에 관련된 변수 및 상수 정의를 sftpHdr.h에 저장
하라
– 서버와 클라이언트는 sftpHdr.h를 include하여 코드를 구현한다
28
심화과제 – Simple FTP
• 심화과제
– 위의 기본 명령어 put, get이외에 다음의 명령어를 추가하라
•
•
•
•
•
ls: client의 디렉토리 목록 출력
rls: server의 디렉토리 목록 출력
cd : client의 디렉토리 이동
rcd: server의 디렉토리 목록 출력
파일의 업, 다운로드 진행상황을 그래피컬하게(ascii) 표현하라
– 디렉토리 관련 함수
• opendir, readdir, closedir, scandir, chdir…
29
Simple FTP client Scenario
• 아래는 하나의 예, 본인 마음대로 구성할 것
#SFtpClient 203.252.164.143 5000
Welcome to Simple FTP client!
ftp command [p)ut
g)et l)s r)ls e)xit ] -> p
filename to p)ut to server -> test.txt
Sending =>############
test.txt(7554 bytes) uploading success to 203.252.164.143
ftp command [p)ut
g)et r)ls e)xit ] -> e
#
30