Network Programming:
소켓 (socket) 이란 ? The interface that the OS provides to its networking subsystem application layer transport layer (TCP/UDP) network layer (IP) link layer (e.g. ethernet) application layer transport layer (TCP/UDP) network layer (IP) link layer (e.g. ethernet) User Process Socket Internet Application Socket API TCPUDP...
Two Types of Application Processes Communication Datagram Socket (UDP) Collection of messages Connectionless sendto, recvfrom No error/flow control Best effort Stream Socket (TCP) Stream of bytes Connection-oriented connect… send, recv disconnect Error/flow control Reliable
Clients and Servers Client program – Running on end host – Requests service – E.g., Web browser Server program – Running on end host – Provides service – E.g., Web server GET /index.html “Site under construction”
Client “sometimes on” – Initiates a request to the server when interested Web browser on your laptop or cell phone – Doesn’t communicate directly with other clients – Needs to know server’s address Server is “always on” – Handles services requests from many client hosts Web server for the – Doesn’t initiate contact with the clients – Needs fixed, known address GET /index.html “Site under construction”
Socket Addresses Popular applications have well-known ports E.g., port 80 for Web and port 25 for /etc/services Port # in TCP/UDP packet header Destination port #, source port # Well-known vs. ephemeral ports Server has a well-known port (e.g., port 80 for http) Between 0 and 1023 (requires root to use) Client picks an unused ephemeral (i.e., temporary) port Between 1024 and
Uniquely identifying traffic between the hosts Two IP addresses and two port numbers Source/destination IP address Source/destination port Underlying transport protocol (e.g., TCP or UDP) Connection socket pair ( :3479, :80) HTTP Server (port 80) Client Client socket address :3479 Server socket address :80 Client host address Server host address FTP Server (port 21)
Socket Server and Client exchange messages over the network through a common Socket API TCP/UDP IP Ethernet Adapter Server TCP/UDP IP Ethernet Adapter Clients Socket API hardware kernel space user space ports Application Socket API TCPUDP...
struct sockaddr The generic: struct sockaddr { u_short sa_family; char sa_data[14]; }; sa_family specifies which address family is being used determines how the remaining 14 bytes are used The Internet-specific: struct sockaddr_in { sin_family short sin_family; sin_port u_short sin_port; sin_addr struct in_addr sin_addr; char sin_zero[8]; }; sin_family = AF_INET (PF_INET) sin_port: port # (0~65535) sin_addr: IP address sin_zero: unused struct in_addr { // IP addr. __be32 s_addr; // 32 bits };
Byte Ordering What would happen if two computers with different integer byte ordering communicate? … different CPU Example sizeof(int) F5 E4 D3 C2 B1 A0 Big-Endian Little-Endian 0 MAX address int * lptr; (* lptr) = 0x8967F5E4;(* lptr) = 0xE4F56789; lptr
Big-Endian Little-Endian Motorola 68xx, 680x0 Intel IBM Hewlett-Packard DEC VAX Internet TCP/IP Sun SuperSPARC Bi-Endian Motorola Power PC Silicon Graphics MIPS RS 232 AMD
Introduction of a network byte order. Big endian uint16_thtons (uint16_thost16bitvalue) uint32_thtonl (uint32_thost32bitvalue) uint16_tntohs (uint16_tnet16bitvalue) uint32_tntohl (uint32_tnet32bitvalue) For port numbers and IP addresses
Byte Manipulation Functions #include void bzero (void *dest, int n); 반환값 : 없음 void bcopy (const void *src, void *dest, int n); 반환값 : 없음 int bcmp (const void *ptr1, const void *ptr2, int n); 반환값 : 같으면 0, 같지 않으면 0 이 아닌 정수
#include void *memset (void *dest, int c, int n); 반환값 : 메모리 영역 dest 에 대한 포인터 void *memcpy (void *dest, const void *src, int n); 반환값 : 메모리 영역 dest 에 대한 포인터 int memcmp (const void *ptr1, const void *ptr2, int n); 반환값 : 같으면 0, 같지 않으면 음의 정수 또는 양의 정수
Address Conversion Functions #include int inet_aton (const char *strptr, struct in_addr *addrptr); 문자열 IP address (strptr) 를 in_addr 구조체로 변환 반환값 : 성공시 1, 에러시 0 in_addr_t inet_addr (const char *strptr); 문자열 IP addres (strptr) 를 network-byte order 의 바이너리 데이터 (in_addr_t 타입 ) 로 변경하여 리턴 반환값 성공시 32-bit network byte ordered IPv4 주소 에러시 INADDR_NONE char *inet_ntoa (struct in_addr inaddr); Network-byte order 의 바이너리 주소를 ( 문자열 ) IP address 로 변경 반환값 : dotted-decimal 문자열 포인터 ※ a = ascii, n = numeric “ ”
int inet_pton (int af, const char *src, void *dst); converts the character string src into a network address structure in the af address family. (IPv4, IPv6 지원 ) const char *inet_ntop (int af, const void *src, char *dst, socklen_t size); converts the network address structure src in the af address family into a character string. (IPv4, IPv6 지원 ) #include void main(int argc, char *argv[]) { struct in_addr addr; char buf[20]; printf ("demical IP addr : %s\n", argv[1]); inet_pton (AF_INET, argv[1], &addr.s_addr); // “ ” binary printf ("inet_pton(%s) : 0x%x\n", argv[1], addr.s_addr); inet_ntop (AF_INET, &addr.s_addr, buf, sizeof(buf)); printf ("inet_ntop(0x%x) = %s\n", addr.s_addr, buf); } “ ”
#include main() { struct sockaddr_in antelope; char *some_addr; inet_aton (" ", &antelope.sin_addr); // store IP in antelope some_addr = inet_ntoa (antelope.sin_addr); // return the IP printf ("%s\n", some_addr);// prints " " // and this call is the same as the inet_aton() call, above: antelope.sin_addr.s_addr = inet_addr (" "); }
호스트 정보를 얻기 위한 함수 #include struct hostent *gethostbyname (const char *hostname); 문자열 hostname 의 노드에 대한 정보를 리턴 (struct hostent * 타입 ) “network.hanbat.ac.kr” struct hostent { char*h_name;/* 공식적인 호스트 이름 */ char **h_aliases;/* 호스트의 별칭 목록 */ int h_addrtype;/* 호스트 주소 타입 : AF_INET or AF_INET6 */ int h_length;/* 호스트 주소의 길이 : IPv4 의 경우 4 */ char **h_addr_list;/* 호스트 주소 목록, numeric */ };
#include int main (int argc, char *argv[]) { int i; struct hostent *he; struct in_addr **addr_list;
if ((he = gethostbyname(argv[1])) == NULL) { herror("gethostbyname"); return 2; } printf("Official name is: %s\n", he->h_name); printf(" IP addresses: "); addr_list = (struct in_addr **)he->h_addr_list; for(i = 0; addr_list[i] != NULL; i++) { printf("%s \n", inet_ntoa(*addr_list[i])); } printf("\n"); } 실습 16, 17, 19~20
struct hostent *gethostbyaddr (const char *addr, int len, int family); 이진 IP 주소를 사용하여 그 주소에 해당하는 호스트 이름을 찾는다. addr. char * 타입이지만 사실은 in_addr 구조체의 포인터 주소이다 len. IP 주소의 크기이다. IPv4 의 경우는 4 Family 주소 체계를 나타낸다. IPv4 의 경우는 AF_INET (binary address) “
#include int main (int argc, char *argv[]) { struct hostent *he; struct in_addr addr; inet_aton(argv[1], &addr); he = gethostbyaddr(&addr, sizeof(addr), AF_INET); printf("Host name: %s\n", he->h_name); }
#include int gethostname (char *retName, int namelen); 현재의 호스트 이름을 반환
#include main(int argc, char **argv) { char hostname[128]; gethostname(hostname, sizeof(hostname)); printf("My hostname: %s\n", hostname); } 실습 22, 24
#include main(int argc, char *argv[]) { struct in_addr ip; struct hostent *hostentry; char str[50]; if (argc != 2){ printf("Usage: address_conversion [ Ip address ]\n"); exit(0); }
inet_aton(argv[1], &ip); printf("Dotted_decimal to Binary_IP = \t%X\n", ip); hostentry=gethostbyaddr((char *)&ip, sizeof(ip), AF_INET); printf("Binary_IP to Domain_name = \t%s\n", hostentry->h_name); memcpy(str, hostentry->h_name, strlen(hostentry->h_name)); // copy host name to str str[strlen(hostentry->h_name)] = '\0'; memset(hostentry, 0, sizeof(struct hostent));// clear hostentry memset(&ip, 0, sizeof(ip));// clear ip hostentry=gethostbyname(str); memcpy(&ip.s_addr, hostentry->h_addr_list[0], sizeof(ip.s_addr));// set ip.s_addr printf("Domain_name to Binary_IP = \t%X\n", ip); printf("Binary_IP to Dotted_decimal = \t%s\n", inet_ntoa(ip)); }
실습
서비스 정보를 얻기 위한 함수 #include struct servent *getservbyname (const char *servname, const char *protoname); 반환값 성공시 호스트의 정보가 담긴 구조체 포인터 에러시 NULL struct servent *sptr; sptr = getservbyname (“domain”, “udp”); sptr = getservbyname (“ftp”, “tcp”); sptr = getservbyname (“ftp”, NULL); sptr = getservbyname (“ftp”, “udp”);// 에러. ftp 는 udp 를 사용하지 않는다 */ struct servent{ char *s_name;/* 공식적인 서비스 이름 */ char **s_aliases;/* 별칭 목록 */ int s_port;/* 포트번호, network-byte order */ char *s_proto;/* 사용되는 프로토콜 */ };
#include struct servent *getservbyport (int port, const char *protoname); 반환값 성공시 호스트의 정보가 담긴 구조체 포인터 에러시 NULL struct servent*sptr; sptr = getservbyport (htons(53), “udp”); sptr = getservbyport (htons(21), “tcp”); sptr = getservbyport (htons(21), NULL); sptr = getservbyport (htons(21), “udp”);// 에러. ftp 는 udp 를 사용하지 않는다
#include main(int argc, char *argv[]) { struct in_addr ip; struct servent *service; int port; if (argc != 3) { printf("Usage: getservice [service] [port]\n"); exit(0); }
if ( (service = getservbyname(argv[1], NULL)) == NULL) { printf("getservbyname error\n"); exit(-1); } port = ntohs(service->s_port); printf("%s port number: %d(%s)\n", service->s_name, port, service->s_proto); memset(service, 0, sizeof(struct servent)); port = htons(atoi(argv[2])); if ((service = getservbyport(port, NULL)) == NULL) { printf("getservbyname error\n"); exit(-1); } printf("port %d(%s) : %s service\n", ntohs(service->s_port), service->s_proto, service->s_name ); } 실습
SOCKET API
전체 과정 above TCP
Above UDP
기본적인 소켓 함수들 #include int socket (int family, int type, int protocol); Create socket 반환값 : 성공시 소켓 기술자, 에러시 -1 Familytype Protocol 일반적으로 0 family 설 명 AF_INET AF_INET6 AF_LOCAL AF_ROUTE AF_KEY AF_ISO IPv4 protocols IPv6 protocols Unix domain protocols Routing sockets Key socket OSI protocols type 설 명 SOCK_STREAM SOCK_DGRAM SOCK_RAW Stream socket (TCP) Datagram socket (UDP) Raw socket Application Socket API TCPUDP...
#include int connect (int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); Client 가 수행 Connect to server (in servaddr) 반환값 : 성공시 0, 에러시 -1
#include int bind (int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); Server 가 실행 Bind address/port (in myaddr) to socket (in sockfd) 반환값 : 성공시 0, 에러시 -1 Wildcard 주소의 예 struct sockaddr_in servaddr; servaddr.sin_addr.s_addr = htonl (INADDR_ANY); /* wildcard */ 자신의 IP 주소를 자동적으로 찾아줌 여러 개의 NIC 을 가지고 있는 경우 각 NIC 에는 하나씩의 IP 주소가 할당됨 이때, INADDR_ANY 는 모든 NIC 에 할당된 IP 주소들 전체를 의미
Many client requests may arrive Server cannot handle them all at the same time Server could reject the requests, or let them wait #include int listen (int sockfd, int backlog); Server 가 실행 backlog Define how many connections can be pending Listen is non-blocking returns immediately 반환값 성공시 0 에러시 -1
Server Waits for connection request to arrive Blocking until the request arrives And then accepting the new request #include int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); Server 가 실행 Accept a new connection from a client 반환값 새로운 연결소켓 기술자 에러시 -1 리턴 후, cliaddr 에는 연결한 클라이언트의 주소 정보가 저장된다.
#include int close (int sockfd); (read, write 를 포함하는 ) sockfd 의 종료 반환값 : 성공시 0, 에러시 -1 int shutdown (int sockfd, int howto); sockfd 의 종료 close 보다 미세한 조정 가능 Howto SHUT_RD : 읽기만을 종료 SHUT_WR : 쓰기만을 종료 SHUT_RDWR : 읽기 / 쓰기 모두 종료 반환값 : 성공시 0, 에러시 -1
데이터의 송수신을 처리하기 위한 함수들 #include int write (int sockfd, const void *buff, int nbytes); TCP 반환값 : 성공시 송신된 실제 바이트 수, 에러시 -1 #include int send (int sockfd, const void *buff, int nbytes, int flags); TCP int sendto (int sockfd, const void *buff, int nbytes, int flags, const struct sockaddr *to, socklen_t addrlen); UDP 반환값 : 성공시 송신된 실제 바이트 수, 에러시 -1
Flags 보통의 경우는 0 flags 설명 recv/send MSG_DONTROUTE 경로배정 처리를 하지 않는다.( 소켓 옵션참고 ) send MSG_DONTWAIT send, sendto, recv, recvfrom 함수에서 blocking 되지 않는다. recv/send MSG_OOB 대역외 데이터의 송수신시 사용된다. recv/send MSG_PEEK recv 혹은 recvfrom 함수 호출이 끝난 후 시스템이 데이터를 버리지 않고 살펴볼 수 있게 한다. recv MSG_WAITALL 수신 함수에 지정한 바이트만큼 읽기 전에는 함수에서 돌아오지 못하도록 지시한다. recv
#include int read (int sockfd, void *buff, int nbytes); TCP 반환값 : 성공시 수신된 실제 바이트 수, 에러시 -1 #include int recv (int sockfd, void *buff, int nbytes, int flags); TCP int recvfrom (int sockfd, void *buff, int nbytes, int flags, struct sockaddr *from, socklen_t *addrlen); UDP 데이터가 수신되면, ( 데이터를 송신한 ) 상대방의 주소가 from 에 저장된다.
TCP EXAMPLES
Simple socket example Basically the client connects to the server, the server sends the message “Hello World”, and the client prints the received message. connection Client Server “Hello World” “Hello world”
simpleServer.c #include int main(){ int welcomeSocket, newSocket; char buffer[1024]; struct sockaddr_in serverAddr; struct sockaddr_in serverStorage; socklen_t addr_size; /*---- Create the socket. ----*/ welcomeSocket = socket(AF_INET, SOCK_STREAM, 0);
/*---- Configure settings of the server address struct ----*/ serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(7891); serverAddr.sin_addr.s_addr = inet_addr(" "); memset(serverAddr.sin_zero, '\0', sizeof (serverAddr.sin_zero)); /*---- Bind the address struct to the socket ----*/ bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr)); /*---- Listen on the socket, with 5 max connection requests queued ----*/ if(listen(welcomeSocket, 5)==0) printf("Listening\n"); else printf("Error\n");
/*---- Accept call creates a new socket for the incoming connection ----*/ addr_size = sizeof (serverStorage); newSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size); /*---- Send message to the socket of the incoming connection ----*/ strcpy(buffer, "Hello World\n"); send(newSocket, buffer, 13, 0); }
simpleClient.c #include int main(){ int clientSocket; char buffer[1024]; struct sockaddr_in serverAddr; socklen_t addr_size; /*---- Create the socket. ----*/ clientSocket = socket(AF_INET, SOCK_STREAM, 0);
/*---- Configure settings of the server address struct ----*/ serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(7891); serverAddr.sin_addr.s_addr = inet_addr(" "); memset(serverAddr.sin_zero, '\0', sizeof (serverAddr.sin_zero)); /*---- Connect the socket to the server using the address struct ----*/ addr_size = sizeof (serverAddr); connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size); /*---- Read the message from the server into the buffer ----*/ recv(clientSocket, buffer, 1024, 0); /*---- Print the received message ----*/ printf("Data received: %s", buffer); }
실습
Iterative daytime 서버와 클라이언트 connection Client Server time Print time “connection from...” Get time
Iterative daytime 서버와 클라이언트 inet.h #include
daytime server - daytime_ser_tcp.c #include "inet.h" #include #define MSGLEN 128 int main(int argc, char *argv[]) { int sockfd, confd; struct sockaddr_in servaddr, cliaddr; int len; char buf[MSGLEN]; time_t date; if (argc != 2) { printf("Usage: daytime_ser [Port number]\n"); exit(0); }
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ printf("server: can't create socket\n"); exit(-1); } servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wild card */ servaddr.sin_port = htons(atoi(argv[1])); if ( (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0){ printf("server: bind error\n"); exit(-1); } listen(sockfd, 5);
while(1) { len = sizeof(cliaddr); confd = accept(sockfd, (struct sockaddr *)&cliaddr, &len); if (confd < 0) { printf("server: accept error\n"); exit(-1); } printf("connection from %s, port %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, buf, sizeof(buf)), ntohs(cliaddr.sin_port)); date = time(NULL);// date the number of seconds since the memset(buf, '\0', sizeof(buf)); sprintf(buf, "%s", ctime(&date)); // buf calendar time (“ Mon Aug 13 08:23: “) send(confd, buf, strlen(buf), 0); close(confd); }
daytime client - daytime_cli_tcp.c #include "inet.h" #include #define MSGLEN 128 int main(int argc, char *argv[]) { int sockfd, clifd; struct sockaddr_in servaddr; int len; char buf[MSGLEN]; time_t date; if (argc != 3){ printf("Usage: daytime_cli [Server IP] [Port number]\n"); exit(0); }
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ printf("client: can't create socket\n"); exit(-1); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr(argv[1]);// server ip addr servaddr.sin_port = htons(atoi(argv[2]));// server port # if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){ printf("client: can't connect\n"); exit(-1); }
memset(buf, '\0', sizeof(buf)); while( (len=recv(sockfd, buf, MSGLEN, 0)) > 0){ buf[len]= '\0'; printf("%s", buf); } close(sockfd); }
실습
Echo client/server connection Client Server echoed data Print Read from socket echo Read from KB data
echoServer.c /*Required Headers*/ #include int main() { char str[100]; int listen_fd, comm_fd; struct sockaddr_in servaddr;
listen_fd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(22000); bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)); listen(listen_fd, 10); comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);
while (1) { bzero(str, 100); read(comm_fd, str, 100); printf("Echoing back - %s", str); write(comm_fd, str, strlen(str)+1); }
echoClient.c #include int main(int argc,char **argv) { int sockfd,n; char sendline[100]; char recvline[100]; struct sockaddr_in servaddr; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof (servaddr));
servaddr.sin_family = AF_INET; servaddr.sin_port = htons(22000); inet_pton(AF_INET, " ", &(servaddr.sin_addr)); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); while(1) { bzero(sendline, 100); bzero(recvline, 100); fgets(sendline, 100, stdin); /*stdin = 0, standard input */ write(sockfd, sendline, strlen(sendline)+1); read(sockfd, recvline, 100); printf("%s", recvline); }
execution 실습
과제 클라이언트와 서버 프로세스간 1:1 로 통신하는 채팅 프로그램을 작성하라. 클라이언트가 키보드로 입력한 메시지는 서버에게 출력되고, 서버가 키보드로 입력한 메시지는 클라이언트에게 출력된다. 클라이언트와 서버는 번갈아 가면서 메시지를 입력한다.
connection Client Server data Print read data Read from KB data 실습
chatServer.c #include int main(int argc, char *argv[]) { int socket_desc, client_sock, c, read_size, portno; struct sockaddr_in server, client; char client_message[100], msg[100];
if (argc < 2) { printf("chatServer port#\n"); exit(0); } portno = atoi(argv[1]); bzero((char *) &server, sizeof(server)); //Create socket socket_desc = socket(AF_INET, SOCK_STREAM, 0); if (socket_desc == -1) { printf("Could not create socket"); } //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = htonl(INADDR_ANY); server.sin_port = htons(portno);
//Bind if (bind(socket_desc, (struct sockaddr *) &server, sizeof(server)) < 0) { perror("bind failed. Error"); return 1; } listen(socket_desc, 3); printf("Waiting for incoming connections...\n"); c = sizeof(struct sockaddr_in); //accept connection from an incoming client client_sock = accept(socket_desc, (struct sockaddr *) &client, (socklen_t *) & c); if (client_sock < 0) { perror("accept failed"); return 1; } printf("Connection accepted\n");
//Receive a message from client bzero(&client_message, sizeof(client_message)); while ((read_size = recv(client_sock, client_message, 100, 0)) > 0) { printf("Client message: %s", client_message); printf("Enter msg to sent to client: "); bzero(&msg, sizeof(msg)); fgets(msg, 100, stdin); //Send the message back to client if (write(client_sock, msg, sizeof(msg)) < 0) { puts("Sorry message failed, Client disconnected"); break; } bzero(&client_message, sizeof(client_message)); }
if (read_size == 0) { puts("Client disconnected"); fflush(stdout); } else if (read_size == -1) { perror("recv failed"); } return 0; }
chatClient.c #include int main(int argc, char *argv[]) { int sock, portno; struct sockaddr_in server; struct hostent *host; char message[100], server_reply[100]; if (argc < 3) { printf("chatClient serverIP serverPort\n"); exit(0); }
portno = atoi(argv[2]); host = gethostbyname(argv[1]); if (!host) { herror(argv[1]); exit(-1); } //Create socket sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { printf("Could not create socket"); } puts("Socket created"); server.sin_addr.s_addr = *(long *) (host->h_addr); server.sin_family = AF_INET; server.sin_port = htons(portno);
//Connect to remote server if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) { perror("connect failed. Error"); return 1; } printf("Connected\n"); printf("Enter message: "); bzero(&message, sizeof(message)); fgets(message, 100, stdin); //Send some data if (send(sock, message, strlen(message), 0) < 0) { puts("Send failed"); return 1; } bzero(&server_reply, sizeof(server_reply));
//Receive a reply from the server while (recv(sock, server_reply, 100, 0) > 0) { printf("Server reply : %s", server_reply); bzero(&server_reply, sizeof(server_reply)); printf("Enter message('bye' to quit): "); bzero(&message, sizeof(message)); fgets(message, 100, stdin); if (strcmp(message, "bye\n") == 0) break; if (send(sock, message, strlen(message), 0) < 0) { puts("Send failed"); return 1; } close(sock); return 0; }
실습
FILE IO
기본 내용 파일 하드디스크에 저장되어 있음 응용 프로세스 (process) 에서 바이트 단위로 입출력 파일에서 데이터를 읽고 쓸 때의 과정 파일을 열고 fopen 열린 파일의 데이터를 읽거나 쓰고 fscanf, fprintf 파일을 닫음 fclose 다 사용한 후에 82 HD HW OS (Kernel) APP.
File open FILE *fopen ( const char *filename, const char *mode ); filename 읽거나 쓸 파일의 이름 mode “r” 읽기 (read) 모드로 파일을 연다. 파일이 없으면 에러가 발생하고, NULL 값을 돌려준다. 파일이 있으면, marker 가 파일의 첫 부분을 가리킨다. marker: 다음에 읽을 위치 파일 오픈 후, 파일에 쓰려고 하면 에러 발생 “w” 쓰기 (write) 모드로 파일을 연다. 파일이 없으면 새로 만든다. 기존의 파일이 있으면 그 이전의 내용은 삭제된다. Marker 는 파일의 첫 부분을 가리킴. 파일 오픈 후, 파일에서 읽으려고 하면 에러 발생 “r+” 또는 “w+” : 읽거나 쓰기 용으로 open 83
“a” 추가 쓰기 (append) 모드로 파일을 연다. 파일이 없으면 새로 만든다. 기존의 파일이 있으면 marker 가 파일의 끝을 가리킨다 새로 추가되는 내용은 그 파일의 가장 뒤부터 파일에 추가한다. 파일 오픈 후, 파일에서 읽으려고 하면 에러 발생 Return 값 성공시 : FILE * 실패시 : NULL 예 : 없는 파일을 읽기 모드로 여는 경우 man fopen 예 #include FILE *fp; fp = fopen (“temp.dat”, “w”); 84
File close 파일에 대한 쓰기, 읽기 등의 작업이 종료된 후 int fclose ( FILE *fp ); fp 는 fopen () 이 리턴했던 파일 포인터 리턴값 0 은 성공적으로 닫았음을 나타냄 오류가 발생하면, 이 함수는 EOF 를 리턴 85
open/close 전체 예 #include int main (void) { FILE *fp; if ((fp=fopen("basic.txt", "w")) == NULL) { printf (“File open failed. \n"); } // write code... if (fclose(fp) == EOF) { printf("Error closing basic.txt\n"); } return 0; } 프로그램이 정상적으로 실행된 경우 basic.txt 파일이 생성됨 86
fprintf #include int main ( void ) { FILE *fp; if ((fp=fopen("basic.txt", "w")) == NULL) { printf ("The file (basic.txt) is not opened. \n"); } fprintf(fp, "My code name is %c, my code is %d.\n", 'Z', 123); if (fclose(fp) == EOF) { printf("Error closing basic.txt\n"); } return 0; } 87 실습
fscanf #include int main (void) { FILE *fp; char ch; int ret; if ((fp=fopen("basic.txt", "r")) == NULL) { printf ("The file ( basic.txt ) is not opened. \n"); } do { ret = fscanf (fp, "%c", &ch); // fscanf 는 읽은 항목 수를 리턴 if (ret == EOF)// file 의 끝까지 읽은 후에는 EOF 를 리턴 break; printf ("%c", ch); } while (1); if (fclose(fp) == EOF) { printf("Error closing basic.txt\n"); } return 0; } 88 실습
#include int main (void) { FILE *fp; char str[128]; int ret; if ((fp=fopen("basic.txt", "r")) == NULL) { printf ("The file ( basic.txt ) is not opened. \n"); } do { ret = fscanf (fp, "%s", str); if (ret == EOF) break; printf ("%s\n", str); } while (1); if (fclose(fp) == EOF) { printf("Error closing basic.txt\n"); } return 0; } 89
과제 간단한 파일전송 클라이언트 - 서버 (simple FTP) 클라이언트는 서버에게 자신이 원하는 파일 이름을 전송한다. 서버는 클라이언트로부터 수신한 파일이름의 파일 내용을 읽어서, 클라이언트에게 전송한다. 클라이언트는 서버로부터 수신되는 파일 내용을 자신의 하드디스크에 저장한다. 90
91 connection Client Server File data Save file contents Read file data Write file data File name “file name” 과제
IO BLOCKING
문제점 서버가 n 개의 client 프로세스와 통신을 하는 경우 어느 클라이언트에서 데이터가 송신될지는 알 수 없는 경우 서버가 1 번 클라이언트에서 read 하면 1 번은 데이터를 송신하지 않은 경우 서버 blocking 3 번은 데이터를 송신하였음 서버가 이미 1 번 클라이언트로부터 데이터를 읽기 위해서 blocking 되었으므로, 읽혀지지 않음 1 번이 데이터를 보낼 때까지 서버는 blocking 해결방안 Non-blocking read Read loop, fcntl() with O_NONBLOCK select (2)
select(2): I/O Multiplexing Waits on multiple file descriptors and timeout The select() system call allows us to use blocking I/O on a set of descriptors (file, socket, …). For example, we can ask select to notify us when data is available for reading on either STDIN or a TCP socket. Returns when any file descriptor is ready to be read or written or indicate an error, or timeout exceeded advantages simple application does not consume CPU cycles while waiting
#include int select (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout); maxfdp1 descriptors (0, 1,... maxfds-1) will be tested 보통 가장 큰 소켓번호에 1 을 더하여 사용 readset a set of fds we want to check if data is available returns a set of fds ready to read writeset returns a set of fds ready to write exceptset returns a set of fds with exception conditions
Timeout if NULL, wait forever and return only when one of the descriptors is ready for I/O otherwise, wait up to a fixed amount of time specified by timeout if we don’t want to wait at all, create a timeout structure with timer value equal to 0 struct timeval { longtv_sec;/* seconds */ longtv_usec;/* microseconds */ }; /* 0.5 초 동안 sleep 하는 코드 */ struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = ; /* 0.5 초 */ select(0, NULL, NULL, NULL, &timeval);
반환값 성공시 감지된 소켓기술자의 수 타임아웃시 0 에러시 -1
fd_set 집합을 다루기 위해 사용되는 4 개의 매크로 매크로설명 void FD_ZERO (fd_set *fdset); fdset 의 모든 비트를 지운다. void FD_SET (int fd, fd_set *fdset) fdset 중 소켓 fd 에 해당하는 비트를 on(1) 시킨다 void FD_CLR (int fd, fd_set *fdset); fdset 중 소켓 fd 에 해당하는 비트를 off(0) 시킨다 int FD_ISSET (int fd, fd_set *fdset); fdset 중 소켓 fd 에 해당하는 비트가 on 되어 있으면 양수값을 반환한다
Pseudo code int s1, s2; /* socket descriptors */ fd_set readfds;/* used by select() */ /* create and bind s1 and s2 */
while(1) { FD_ZERO(&readfds);/* initialize the fd set */ FD_SET(s1, &readfds);/* add s1 to the fd set */ FD_SET(s2, &readfds);/* add s2 to the fd set */ if(select(s2+1, &readfds, 0, 0, 0) < 0) { perror(“select”); exit(1); } if(FD_ISSET(s1, &readfds)) { recvfrom(s1, buf, sizeof(buf),...); /* process buf */ } /* do the same for s2 */ }
select 함수의 사용 예 select 함수를 사용한 TCP Concurrent echo 서버 - 클라이언트
echo_ser_tcp_select.c #include "inet.h" #include #define MSGLEN 1024 #define MAXCLI 50 int main(int argc, char *argv[]) { int sockfd, confd; struct sockaddr_in cliaddr, servaddr; int addrlen; fd_set read_fdset;
int client_fd[MAXCLI]; int client_num = 0; char buf[MSGLEN]; int msg_len, maxfdp1, nready, i; pid_t childpid; if (argc != 2){ printf("Usage:echo_ser_select [Port number]\n"); exit(0); } if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ printf("server: can't create socket\n"); exit(-1); }
servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wild card */ servaddr.sin_port = htons(atoi(argv[1])); if ((bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0){ printf("server: bind error\n"); exit(-1); } listen(sockfd, 5); for (i=0; i<MAXCLI; i++) client_fd[i] = -1; FD_ZERO(&read_fdset); maxfdp1 = sockfd + 1;
while(1) { FD_SET(sockfd, &read_fdset); for (i=0; i<MAXCLI; i++){ if (client_fd[i] != -1) FD_SET(client_fd[i], &read_fdset); } if ((nready=select(maxfdp1, &read_fdset, NULL, NULL, NULL) )< 0){ printf("select error..\n"); exit(-1); }
if (FD_ISSET(sockfd, &read_fdset)){ addrlen = sizeof(cliaddr); confd=accept(sockfd,(struct sockaddr *)&cliaddr, &addrlen); if (confd < 0){ printf("server: accept error\n"); exit(-1); } if (client_num >= MAXCLI) { printf(“too many clients\n”); close(confd); continue; } printf("connection new client(confd=%d)...\n", confd); for (i=0; i<MAXCLI; i++) { if (client_fd[i] != -1) continue; client_fd[i] = confd;maxfdp1++; client_num++;break; }
for ( i=0; i<MAXCLI; i++) { if (client_fd[i] == -1) continue; if (FD_ISSET(client_fd[i], &read_fdset)) { memset(buf, '\0', sizeof(buf)); msg_len = read(client_fd[i], buf, MSGLEN); if ( write(client_fd[i], buf, msg_len) != msg_len ) { printf("server: write error!!\n"); close(confd); exit(0); } if ( --nready <= 0) /* 더 이상 변화된 소켓기술자가 없다면 */ break; } } /* end of the 'while' */ }
echo_cli_tcp_select.c #include "inet.h" #include #define MSGLEN 1024 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in servaddr; fd_set read_fdset, reset; char buf[MSGLEN]; int len, msg_len, rcvlen, maxfdp1; if (argc != 3) { printf("Usage: echo_cli_select [Server IP] [Port number]\n"); exit(0); }
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ printf("client: can't create socket\n"); exit(-1); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr(argv[1]); servaddr.sin_port = htons(atoi(argv[2])); if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){ printf("client: can't connect\n"); exit(-1); }
FD_ZERO(&read_fdset); FD_SET(0, &read_fdset); FD_SET(sockfd, &read_fdset); reset = read_fdset; maxfdp1 = sockfd + 1;
while(1){ read_fdset = reset; if (select(maxfdp1, &read_fdset, NULL, NULL, NULL) < 0) { printf("select error..\n"); exit(-1); } if (FD_ISSET(0, &read_fdset)){ memset(buf, '\0', sizeof(buf)); if ( (msg_len = read(0, buf, sizeof(buf))) <= 0) { close(sockfd);exit(0); } if ( write(sockfd, buf, msg_len) != msg_len ) { printf("client: write error!!\n"); close(sockfd); exit(0); }
if (FD_ISSET(sockfd, &read_fdset)) { memset(buf, '\0', sizeof(buf)); len=0; do { if ((rcvlen=read(sockfd,&buf[len],msg_len-len)) <= 0){ close(sockfd); exit(0); } len += rcvlen; } while(len < msg_len); printf("echo : %s", buf); }
UDP EXAMPLES
UDP echo client/server Client Server echoed data Print Read from socket echo Read from KB data
UDP echo client/server udpEchoServer.c #include #define BUFLEN 512 //Max length of buffer #define PORT 8888 //The port on which to listen for incoming data void die (char *s) { perror(s); exit(1); }
int main(void) { struct sockaddr_in si_me, si_other; int s, i, slen = sizeof(si_other), recv_len; char buf[BUFLEN]; if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { die("socket"); } memset((char *) &si_me, 0, sizeof(si_me)); si_me.sin_family = AF_INET; si_me.sin_port = htons(PORT); si_me.sin_addr.s_addr = htonl(INADDR_ANY); if( bind(s, (struct sockaddr*)&si_me, sizeof(si_me) ) == -1) { die("bind"); }
while(1) { printf("Waiting for data..."); fflush(stdout); //try to receive some data, this is a blocking call if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1) { die("recvfrom()"); } buf[recv_len] = 0;// set null char. //print details of the client/peer and the data received printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port)); printf("Data: %s\n", buf);
//now reply the client with the same data if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1) { die("sendto()"); } }// while close(s); return 0; }
udpEchoClient.c #include #define SERVER " " #define BUFLEN 512 //Max length of buffer #define PORT 8888 //The port on which to send data void die(char *s) { perror(s); exit(1); }
main(void) { struct sockaddr_in si_other;int s, i, slen=sizeof(si_other); char buf[BUFLEN];char message[BUFLEN]; if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { die("socket"); } memset((char *) &si_other, 0, sizeof(si_other)); si_other.sin_family = AF_INET; si_other.sin_port = htons(PORT); if (inet_aton(SERVER, &si_other.sin_addr) == 0) { fprintf(stderr, "inet_aton() failed\n"); exit(1); }
while(1) { printf("Enter message : ");fgets(message, 512, stdin); if (sendto(s, message, strlen(message), 0, (struct sockaddr *) &si_other, slen)==-1) { die("sendto()"); } //clear the buffer by filling null, it might have previously received data memset(buf, '\0', BUFLEN); //try to receive some data, this is a blocking call if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1) { die("recvfrom()"); } puts(buf); }
close(s); return 0; }
실습
Iterative echo 서버와 클라이언트
echo_ser_iter_udp.c #include "inet.h" #define MSGLEN 128 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in servaddr, cliaddr; int addrlen, msg_len; char clidot[20], buf[MSGLEN]; time_t date; if (argc != 2){ printf("Usage: echo_ser_iter_udp [Port number]\n"); exit(0); }
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ printf("server: can't create socket\n"); exit(-1); } servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wild card */ servaddr.sin_port = htons(atoi(argv[1])); if ( (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0){ printf("server: bind error\n"); exit(-1); }
while(1) { memset(buf, '\0', sizeof(buf)); addrlen = sizeof(cliaddr); msg_len = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &addrlen); if (msg_len < 0) continue; printf("client: %s, port %d message length: %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, clidot, sizeof(clidot)), ntohs(cliaddr.sin_port), msg_len); if (sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr))!= msg_len) printf("sendto error\n"); }
echo_cli_udp.c #include "inet.h" #include #define MSGLEN 128 int main(int argc, char *argv[]) { int sockfd, clifd;struct sockaddr_in servaddr; fd_set read_fdset, reset; int addrlen, len, msg_len, maxfdp1; char buf[MSGLEN]; time_t date; if (argc != 3){ printf("Usage: echo_cli_dup [Server IP] [Port number]\n"); exit(0); }
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ printf("client: can't create socket\n"); exit(-1); } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr(argv[1]); servaddr.sin_port = htons(atoi(argv[2])); FD_ZERO(&read_fdset); FD_SET(0, &read_fdset); FD_SET(sockfd, &read_fdset); reset = read_fdset; maxfdp1 = sockfd + 1;
while(1){ read_fdset = reset; if (select(maxfdp1, &read_fdset, NULL, NULL, NULL) < 0){ printf("select error..\n"); exit(-1); }
if (FD_ISSET(0, &read_fdset)){ memset(buf, '\0', sizeof(buf)); if ( (msg_len = read(0, buf, sizeof(buf))) <= 0){ close(sockfd); exit(0); } if (sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr)) != msg_len){ printf("sendto error\n"); close(sockfd); exit(-1); }
if (FD_ISSET(sockfd, &read_fdset)){ memset(buf, '\0', sizeof(buf)); addrlen = sizeof(servaddr); len = recvfrom(sockfd, buf, MSGLEN, 0,(struct sockaddr *) &servaddr, &addrlen); printf("echo : %s", buf); } } /* end of the ‘while’ */ }
Concurrent echo 서버
echo_ser_con_udp.c #include "inet.h" #define MSGLEN 128 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in servaddr, cliaddr; int addrlen, msg_len; char clidot[20], buf[MSGLEN]; time_t date; if (argc != 2) { printf("Usage: echo_ser_iter_udp [Port number]\n"); exit(0); }
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){ printf("server: can't create socket\n"); exit(-1); } servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wild card */ servaddr.sin_port = htons(atoi(argv[1])); if ( (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))) < 0){ printf("server: bind error\n"); exit(-1); }
while(1) { memset(buf, '\0', sizeof(buf)); addrlen = sizeof(cliaddr); msg_len = recvfrom (sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&cliaddr, &addrlen); if (msg_len < 0) continue; if (fork() == 0){ printf("client: %s, port %d message length: %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, clidot, sizeof(clidot)), ntohs(cliaddr.sin_port), msg_len); if (sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr))!= msg_len) printf("sendto error\n"); return 0; } /* end of the ‘child’ */ } /* end of the 'while' */ }