580 likes | 801 Views
Effective TCP/IP Programming. Snader, ETCP 중심으로. Understand IPv4 Addressing. IPv4: originally Classful Addressing. Special address Host ID = 0: network address Network ID = 0, host ID = 0 i.e 0.0.0.0 means this network 127.x.y.z : “looped back” Host ID = all 1s : broadcasting
E N D
Effective TCP/IP Programming Snader, ETCP 중심으로
IPv4: originally Classful Addressing • Special address • Host ID = 0: network address • Network ID = 0, host ID = 0 • i.e 0.0.0.0 means this network • 127.x.y.z : “looped back” • Host ID = all 1s : broadcasting • NAT address • 10/8 • 172.16/12 • 192.168/12 • Broadcast address • 255.255.255.255 • Limited broadcast • router 밖에는 나가지 못함 • 190.50.255.255 • Network-directed broadcast • 그 네트워크까지 가서 네트워크 내에 broadcast • 190.50.1.255/24 • Subnet-directed broadcast • 190.50.1.0/24 subnet 내에 broadcast IPv4 address는 host의 address 가 아니라 interface의 address이다.
223.1.0.0/16 Subnet 223.1.1.0/24 223.1.2.0/24 • Network address IP address && mask • Network ID 223.1.1.1 223.1.2.1 223.1.1.2 223.1.2.9 223.1.1.4 223.1.2.2 223.1.1.3 223.1.3.27 subnet 223.1.3.2 223.1.3.1 223.1.3.0/24 network consisting of 3 subnets • CIDR (Classless Inter-Domain Routing) • Subnetting + suppernetting
Making UNIX/Windows Compatible UNIX: bsd/skel.h Windows: win/skel.h wincompat.c: Window에서 socket을생성전에 call해야
“mclab.hufs.ac.kr” or “203.254.68.114” or “” -- server “http” or “80” TCP Server Skeleton tcpserver.skel:
TCP Server Skeleton - Cont’d Usage: % myserver [local_addr | local_name] {local_port | service} Example: %myserver 15000 %myserver localhost http
TCP Client Skeleton tcpclient.skel: Usage: % myclient {peer_addr | peer_name} {peer_port | service} Example: % myclient 203.253.70.5 15000 % myclient www.hufs.ac.kr http
UDP Server & Client Skeleton udpserver.skel: udpclient.skel:
TCP Client & Server Starting Functions Host name or IP addr or “” (my addr for server) “http” or “80”
TCP is a Stream Protocol • No message boundary, just a byte stream • TCP application에서 하나의 message를 send()했다고 해서 recevier application에서 그 message를 한 덩어리로 recv()되는 것이 아니다. • Message를 send()했어도 TCP segment에 언제, 어떻게 실릴지 모른다. (buffering 되기 때문) • Recv()하면 몇 byte 읽힐지 모른다. • If you want to read exactly n bytes ?? • If you want to make a record ??? • Use end-of-record mark. e.g) new line • Handle variable records (using fixed header) 메시지를 읽을 user buffer의 크기 즉, 최대로 읽을 수 있는 크기 #include <sys/socket.h> /* UNIX */ #include <winsock2.h> /* Windows */ int recv (SOCKET s, void *buf, size_t bufsize, int flags); int read (SOCKET s, void *buf, size_t bufsize); /* UNIX */ Returns: # of bytes read (>0), 0 if received FIN and no more data, -1 on failure int send (SOCKET s, const void *buf, size_t len, int flags); int write (SOCKET s, const void *buf, size_t len); /* UNIX */ Returns: # of bytes transferred on success, -1 on failure Socket send buffer에 저장할 메시지 길이
Use End-of-record mark: read a line lib/readline.c:
Read n bytes and Read a variable-length record lib/readvrec: Network byte order lib/readn.c: Header size len cnt bp #include “etcp.h” int readn (SOCKET s, char *buf, size_t len); Returns: # of bytes read, -1 on failure int readvrec (SOCKET s, char *buf, size_t len); Returns: # of bytes read , -1 on failure Record Length Other Header Data Variable Data
Example: Client/Server using variable records vrs.c: vrc.c:
TCP connection-oriented reliable byte stream Application: typically concurrent server SMTP(Simple Mail Transfer Protocol) Telnet FTP HTTP NNTP(Network News TP) UDP connectionless unreliable datagram Applications: typically iterative server SNMP(Simple Network Management Protocol) TFTP(Trivial FTP) BOOTP(Bootstrap Protocol) DHCP(Bootstrap Protocol) TCP versus UDP
TCP는 충분히 Optimize 되어 있다. • 통상적인 TCP segment 수신 루틴: 30 instructions (excluding checksum) • ACK도 piggy-back • 섯불리 UDP를 사용하지 말라. • But, a single request-response로 끝나는 transaction에서는 TCP는 • Connection set-up: RTT 소요 • Connection release: 적어도 RTT 소요
A UDP Source and Sink 실제 5,000개의 datagram을 buffering할 수는 없다. 왜냐 하면, 시스템에서 허용하는 socket receiver buffer의 최대 크기는 이보다 훨씬 작기때문이다(수십 KB). udpsource.c: udpsink.c: Set UDP socket receive buffer size Record of length 0 i.e. end-of-record mark • UDP datagram은 lost될 수 있다!! • Network congestion (nocongestion cotrol) • Recv buffer overflow(no flow control)
A TCP Source and Sink tcpsink.c: tcpsource.c: Options: -s sndsz -b sndbufsz -c blks Set TCP receive buffer size Set TCP send buffer size
Comparison of TCP and UDP Performance • LAN에서 UDP가 TCP보다 20% 정도 성능이 우수했음 • 그러나, UDP에서는 lost가 많이 발생함 (no flow control) • Loopback interface(같은 host 내)에서는 TCP가 UDP 보다 훨씬 성능 우수했음 • Local host의 MTU는 16,384 B (BSD) • Ethernet의 MTU는 1,500 B
Avoid Reinventing TCP • Any reasonably robust UDP application must provide • Error recovery: reTx a request if not received a response within RTO • Sequencing: ensure that replies are matched correctly to requests • Flow control: if server’s reply can consist of multiple datagrams, prohibit overflow of client’s recv buffer • Cause to rewrite TCP • TCP는 kernel에서 처리되기 때문에 application에서 reliable protocol을 구현하는 것보다 실제 빠르다.
When to Use UDP instead of TCP • Adv. Of UDP • supports broadcasting and multicasting • no overhead for connection setup or teardown • UDP requires 2 packets to exchange a request and a reply • TCP requires about 10 packets to exchange assuming new TCP connection is established for each request-reply exchange • Features of TCP that are not provided by UDP • positive ACK, reTx of lost packet, duplicate packet detection, sequencing of packets • windowed flow control • slow start and congestion avoidance • Recommendation of UDP Usage • must be used for broadcast or multicast applications • desired level of error control must be added • can be used for simple request-reply applications • error detection must be needed • should not be used for bulk data transfer
Realize that TCP is a Reliable Protocol, Not Infallible Protocol
TCP is a Reliable Protocol, Not Infallible Protocol • 2 peer간에 connection이 유지되는 한 TCP는 ordered and uncorrupted delivery를 보장한다. • Application은 통신이 불가능함을 모르고 데이터를 보낼 수 있고, 따라서 목적지에 delivery 되지 못하는 경우가 발생한다. • TCP는 data를 보내봐야 실제 peer TCP와 통신 가능한지 확인 가능 (ACK를 받아 봐야) 또는, 2시간 이상 데이터 교환이 없을 경우에나 통신 불가능을 확인 할 수 있음 • 교환할 데이터가 없어도 주기적으로 교환해야 heart beat mechanism 구현 필요 • Application은 send()/recv()가 error return 되었을 때야, 통신 불가능함을 알게 된다. failure 처리 • 통신 불가능한 경우 (실제 connection이 유지되지 않는 경우) • Network outage (due to router or link failure) • Peer app crashes • Peer host crashes
Network Outage • Inside TCP • Segment 보낸 후 ACK 가 없으면, 12번 ReTx한다 (약 9분 걸림) • 여전히 ACK를 받지 못하면, set socket pending error (ETIMEOUT) • Inside IP/ICMP • IP datagram을 forwarding할 수 없으면(router나 link 장애로 인해), ICMP host unreachable/network unreachable message를 source로 보낸다. • 이 메시지를 Source IP가 받으면, set socket pending error (ENETUNREACH/EHOSTUNREACH) • Socket Pending Error • Send() returns on failure send buffer에 쓰는 것이 실패를의미 • 실제 보낸 데이터가 peer에게 전달할 수 없음은 한참 뒤에나 알 수 있다. • Kernel은 이와 같은 error가 발생하면, 해당되는 socket에 pending 시켜 놓는다 • Socket API call이 이루어질 때, error return하면서 errno에 설정한다.
Peer App Crashes • When peer app crashes, Local app is • In recv(): return 0 • 통상적인 절차로 종료 • In send(): normal return • But, sent data is lost.Local connection을 강제 close.Error is pending. • Send()/recv(): error return (ECONNRESET) • Send()/recv(): rrror return (EPIPE) • Peer app crashes(killed) • Call exit(), implicitly • Call close() in exit() • TCP: send FIN FIN data No connection ! Not delivered to peer app RESET
Peer app crashes – an example tcprw.c: count.c: killed killed
Peer Host Crashes • Local app • Connection set-up • Send(): normal return • But sent data is lost • Error is pending (ETIMEOUT) after retransmitting 12 times(9 min) • or error is pending (EHOSTUNREACH or ENETUNREACH) by ICMP • Peer host crash • No TCP there, so no TCP response data No TCP/IP Protocol !
Remember that TCP/IP is Not Polled No notification when connectivity is lost
Heartbeats • 데이터를 보내 보지 않고서는 peer와 통신 가능 여부를 알 수 없다 • 통신 불가능함에도 불구하고 데이터를 보내면 lost됨(송금했는데 못 받았으면 ???) • 데이터 교환과 별도로 상대가 살아 있는지 주기적으로 check해 봐야 함 hearbeat 필요 • C/S가 여러가지 msg type을 교환하는 경우 • Heartbeat msg에 새로운 type을 할당 • C/S가 byte stream으로 교환하는 경우 • Hearbeat 과 data를 구분할 수 없음 • Hearbeat에 대해 별도 TCP connection 설정하여 구분 • Or, Hearbeat에 대해 OOB msg로 교환 • (send/recv() 에서 flag를 OOB로 설정)
Hearbeat Client – msg type heartbeat.h: hb_client.c:
Heartbeat Server- msg type hb_server.c:
Hearbeat Client – separate connection hb_client2.c:
Hearbeat Server – separate connection hb_server2.c:
Checking for client termination • Checking for valid input • UNIX utility program 중 6 ~ 43 % 가 인위적으로 생성한 random input으로 시험했을 때 죽었음
Read a line lib/readline.c: len 0 bufx bufptr Buffering cnt b bp From socket C string은 ‘\0’를 append해야 함 Line >= 2 bytes
inetd daemon • Problems starting with /etc/rc(without inet daemon) • All the servers contains nearly identical startup code • Each daemon takes a slot in process table, but asleep most of time • /etc/inetd.conf file specifies the services that the superserver inetd is to handle
Steps performed by inetd dup2(sockfd, 0); dup2(sockfd, 1); dup2(sockfd, 2); close(sockfd); Open descriptor들은 fork시 copy되고 Exec 후에도 유지된다. Exec후에 Peer를 알 수 있는 방법은?
Concurrent Input/Output processes • One-connection architecture • Xout에서 send할 때 pending error는 xin에서 recv해야 알 수 있다 • Xin mp xout 으로 알려 줘야 함 • Two connection architecture • 별도의 connection에 대해 socket pending error가 있는지 testing 가능 • Using select() readability
Example xout1.c:
Making Your Apps Event Driven • Types of events in protocols and networked application • Packet arrival • User input • Timeout • API call • 만일, API도 UNIX domain socket(일종의 socket descriptor임)이라면 select()로 API call이 발생했음을 확인 가능 • 다만, select()는 timeout은 하나만 가능 • How to support multiple timeouts using select() ? • You can make it ! select()로 확인 가능 (Ready or timeout? )