<1> <Network Programming> Part 3 Socket Programming for Content Delivery in Multimedia Networks (Unix 기반 )
<2> <Network Programming> 유닉스소켓시스템콜 BSD 소켓 API의소개 IP 주소변환설명 소켓을이용한클라이언트및서버프로그램작성방법소개 유닉스시스템콜 signal() 과 fork() 소개 토크프로그램작성
<3> <Network Programming> 소켓의정의 TCP/IP 를이용하는 API 1982 년 BSD(Berkeley Software Distribution) 유닉스 4.1 윈도우소켓 ( 윈속 ), 자바소켓등도 TCP/IP 를이용하기 위한 API 를제공
<4> <Network Programming> 소켓 소켓소개 IP 어드레스와포트, 소켓의관계
<5> <Network Programming> Socket o UNIX system 을위하여가장많이보급되고있는두가지통신 API - Berkeley socket -C programming language 를위하여개발 o Socket - File I/O 개념을 Network I/O 로확장한것 o Network I/O는 File I/O보다더욱상세하고많은선택사항들을필요로한다. - 망연결을초기화하기위해서프로그램은자신의역할이어떤것인지알아야함. - 망연결은연결지향형또는비연결형이될수있다. - 망연결에필요한매개변수들은 file I/O보다많다. { 규약, 지역주소, 지역프로세스, 상대주소, 상대프로세스 } - 몇몇통신규약에서레코드경계는중요하다. - 망접속은다수의통신규약을지원해야한다.
<6> <Network Programming> Socket APIs Function Socket API Message Queue API File IO API Server Create endpoint socket() msgget() open() Bind address Specify queue Wait for connection bind() listen() accept() Client Create endpoint socket() msgget() open() Bind address Connect to server bind() connect() Common Transfer data read(), write() recv(), send() Transfer datagrams recvfrom() sendto() msgrcv() msgsnd() read() write() terminate close(), shutdown() close()
<7> <Network Programming> C 언어에서 TCP 소켓프로그램작성 connection-oriented protocol
<8> <Network Programming> C 언어에서 UDP 소켓프로그램작성 o connectionless protocol 에대한 socket system calls UDP Server (connectionless protocol) socket() bind() UDP Client recvfrom() socket() blocks until data received from a client data (request) bind() sendto() process request write() data (reply) recvfrom()
<9> <Network Programming> Java 에서 TCP 소켓프로그램작성
<10> <Network Programming> Java 에서 UDP 소켓프로그램작성 자바 UTP 프로그램작성 (DatagramSocket 이용 )
<11> <Network Programming> 소켓번호 소켓개설시리턴되는값 기술자테이블 (descriptor table) 에위치
<12> <Network Programming> 소켓번호 (Cont d)
<13> <Network Programming> 소켓의구조 (1/3) 소켓시스템콜 #include <sys/socket.h> int socket ( int domain, /* 프로토콜체계 */ int type, /* 서비스타입 */ int protocol ); /* 프로토콜 */ Domain PF_INET / AF_INET PF_INET6 / AF_INET6 PF_UNIX / AF_UNIX PF : Protocol Family, AF: Address Family Sockaddr / sockaddr_in의구조체 Type SOCK_STREAM SOCK_DGRAM SOCK_RAW Protocol 0 ( 자동적으로프로토콜이선택되어진다 )
<14> <Network Programming> 소켓의구조 (2/3) 2. Family - 지원하는프로토콜그룹중에서사용하려는소켓이적용될프로토콜을선택
<15> <Network Programming> 소켓의구조 (3/3) 3. Type - 연결지향형과비연결형, 저수준프로토콜제어형 - 저수준프로토콜 IP 프로토콜과같은레벨에있는프로토콜을사용시필요 ex) ICMP (Internet Control Message Protocol) TCP/UDP 보다하위계층으로사용이까다로운반면직접적인제어가가능 4. Protocol 여러소켓형태를제공하는경우사용, 기본값은 0
<16> <Network Programming> 소켓의타입 1. Stream Socket - 연결지향형 (TCP 기반 ) - 소켓간의연결후데이터전송 - 일상생활의전화개념과유사 2. Datagram Socket - 비연결형 (UDP 기반 ) - 송수신시도착지주소필수 - 일상생활의편지개념과유사 3. Raw Socket - 저수준프로토콜액세스 - ICMP, OSPF 등이사용 - IP 계층이용
<17> <Network Programming> 예제 : 소켓생성 open_socket.c 예제 (NetPro/Soc/Util/open_socket.c) int fd1, sd1; fd1 = open( /etc/passwd, O_RDONLY, 0); sd1 = socket(pf_inet, SOCK_STREAM, 0); printf( File Descriptor %d\n, fd1); printf( Socket Descriptor %d\n, sd1); close(fd1); close(sd1); 실행결과 File Descriptor = 3 Socket Descriptor = 4
<18> <Network Programming> 소켓의의미 소켓을이용한데이터송수신시필요한정보전달수단 1. 프로토콜 (TCP or UDP) 2. 자신의 IP 주소 3. 자신의포트번호 4. 상대방의 IP 주소 5. 상대방의포트번호 소켓의구조 : Structure of Socket
<19> <Network Programming> 소켓구조체 소켓의구조체 (1/5) * 네트워크를통하여다른컴퓨터와통신을하기위해서는위와같은다양한속성의설정이필요하기때문에, 이후등장할여러가지속성간의특징파악이필요
<20> <Network Programming> 소켓의구조체 (2/5)
<21> <Network Programming> 소켓의구조체 (3/5) 1. sockaddr 구조체 - 기본적인소켓주소구조체 - 호환성을위해존재 - 변수 : 구조체의크기, Family Type, 소켓의주소데이터 2. sockaddr_in 구조체 - IPv4 소켓주소구조체 - 주소를담기위해 in_addr 구조체사용 - 변수 : 구조체의크기, Family Type, 소켓의주소및포트번호 3. sockaddr_un 구조체 - 유닉스소켓주소구조체 - 동일호스트에서의통신이일반 TCP 통신보다두배빠름 - 변수 : 소켓상수, 호스트경로
<22> <Network Programming> 소켓의구조체 (4/5) struct sockaddr { u_short sa_family; /* address family */ char sa_data[14]; /* 주소 */ } struct in_addr { u_long s_addr; /* 32bit IP 주소 */ } struct sockaddr_in { short sin_family; /* 주소체계 */ u_short sin_port; /* 16bit 포트번호 */ struct in_addr sin_addr; /* 32bit IP 주소 */ char sin_zero[8]; /* dummy */ }
<23> <Network Programming> Unix 내의 Socket Address 관련예 (1/2) o 많은 BSD network system call 은독립변수로서 socket address 구조의지시자를필요로한다. - 이구조의정의는 <sys/socket.h> 에있다. struct sockaddr { u_short sa_family; /* address family: AF_xxx value */ char sa_data[14]; /* up to 14 bytes of protocol-specific address */ }; o 14 byte 의 protocol-specific address 의내용은 address 의 type 에따라서해석 o Internet family 에대하여다음구조가 <netinet/in.h> 에정의 struct in_addr { u_short s_addr; /* 32-bit netid/hostid */ }; struct sockaddr_in { short sin_family; /* AF_INET */ u_short sin_port; /* 16-bit port number */ /* network byte ordered */ struct in_addr sin_addr; /* 32-bit netid/hostid */ /* netowrk byte ordered */ char sin_zero[8]; /* unused */ };
<24> <Network Programming> Unix 내의 Socket Address 관련예 (2/2) o <sys/types.h> 에정의된 unsigned data type C Data type 4.3 BSD System V unsigned char unsigned short unsigned int unsigned long u_char u_short u_int u_long unchar ushort uint ulong o Internet, XNS, Unix family 에대한 socket address 구조 struct sockaddr_in family 2-byte port 4-byte net ID, host ID (unused) struct sockaddr_ns family 4-byte net ID 6-byte host ID 2-byte port (unused) struct sockaddr_un family pathname (up to 108 bytes)
<25> <Network Programming> 소켓의구조체 (5/5) sockaddr_in 구조체를사용 sockaddr 과의호환성을위해 dummy 삽입 sin_family AF_INET 인터넷주소체계 AF_UNIX 유닉스파일주소체계 AF_NS XEROX 주소체계
<26> <Network Programming> 예제 : 소켓구조체사용예 Socket_addr.c 예제 (NetPro/Soc/Util/socket_addr.c) %socket_addr IP_addr Port_no
<27> <Network Programming> 유닉스소켓프로그래밍을위한유틸리티함수들 1. 바이트순서변환함수 2. 주소변환구조체 3. 인터넷주소변환함수 4. 원격지호스트정보를얻는함수
<28> <Network Programming> 1. 바이트순서변환함수 호스트바이트순서 CPU 에따라메모리에저장하는순서가틀림 네트워크바이트순서 CPU 에관계없이 High-order 순서로전송 Internet protocol 을위하여설계 htonl(), htons(), ntohl(), ntohs() htonl convert host-to-network, long integer htons convert host-to-network, short integer ntohl convert network-to-host, long integer ntohs convert network-to-host, short integer #include <sys/types.h> #include <netinet/in.h> u_long u_short u_long u_short htol(u_long hostlong); htons(u_short hostshort); ntohl(u_long netlong); ntohs(u_short netshort);
<29> <Network Programming> 바이트순서 ( 예 ) Big-Endian Little-Endian - 데이터의최상위바이트의내용을저장될메모리의시작주소에저장 - 모토로라마이크로프로세서및 IBM 메인프레임기반 - 데이터의최하위바이트의내용을저장될메모리의시작주소에저장 - DEC VAX 컴퓨터및인텔마이크로프로세서
<30> <Network Programming> 바이트순서 ( 예제프로그램 ) byte_order.c 예제 pmyservent = getservbyname("echo", "udp"); printf("port number of 'echo', 'udp' without ntohs() : %d \n", pmyservent->s_port); printf("port number of 'echo', 'udp' with ntohs() : %d \n", ntohs(pmyservent->s_port)); 실행결과 (x86 Linux System) Port number of 'echo', 'udp' without ntohs() : 1792 Port number of 'echo', 'udp' with ntohs() : 7 실행결과 (mc68000 계열 ) Port number of 'echo', 'udp' without ntohs() : 7 Port number of 'echo', 'udp' with ntohs() : 7
<31> <Network Programming> 2. 주소변환구조체 - 기본적인 IP 주소체계를도메인주소로변환시사용 A. hostent 구조체 - gethostbyaddr(), gethostbyname() 함수에사용 - 변수 : 호스트의공식명칭, 별칭, 호스트주소의종류, 주소체계의바이트수, 도메인에포함된 IP list B. servent 구조체 - getservbyname(), getservbyport() 함수에사용 - 변수 : 서버의공식명칭, 별칭, 서버가제공하는포트번호, 서버가사용하는프로토콜타입의문자열
<32> <Network Programming> 원격지호스트정보를위한구조체 (1) * hostent 구조체
<33> <Network Programming> hostent 구조체정의 struct hostent { char *h_name; /* 호스트이름 */ char **h_aliases; /* 호스트별명들 */ int h_addrtype; /* 호스트주소의종류 */ int h_length; /* 주소의크기 */ char **h_addr_list; /* IP 주소리스트 */ }; #define h_addr haddr_list[0] /* 첫번째주소 */
<34> <Network Programming> 원격지호스트정보를위한구조체 (2) * servent 구조체
<35> <Network Programming> getservbyname() 시스템이현재지원하는 TCP/IP 응용프로그램의정보를알아내는함수로, 서비스이름과프로토콜을인자로주어호출하면서비스관련각종정보를포함하고있는 servent 라는구조체의포인터를리턴한다. pmyservent = getservbyname("echo", "udp"); servent 구조체의정의 (netdb.h 파일참조 ). struct servent { char *s_name; /* 서비스이름 */ char **s_aliases; /* 별명목록 */ int s_port; /* 포트번호 */ char *s_proto; /* 사용하는프로토콜 */ }; servent 구조체는네트워크로부터얻은정보이므로그내용이네트워크바이트순서로되어있고이를화면에출력해보려면호스트바이트순서로바꾸어야한다.
<36> <Network Programming> 3. 인터넷주소 (IP 주소 ) 변환 인터넷주소를표현하는방식 도메인네임 32bit IP 주소 십진수표시법 (dotted decimal) IP 주소변환함수예 mm.sookmyung.ac.kr : : 203.252.201.16 Domain Name : IP 주소 (binary) : dotted decimal gethostbyname() inet_ntoa() gethostbyaddr() inet_addr()
<37> <Network Programming> 주소변환함수 (Address Conversion Routines)-1 unsigned long int inet_addr (const char * strptr); - Dotted Decimal' 문자열형태의주소 (IP 주소 : 예 203.252.201.16) 를네트워크바이트순서를갖는이진바이너리 (cbfcc910) 로바꾸는함수 - 점으로분리된십진수개념의문자열을 32- 비트 Internet 주소로변환 - 입력값이적절치못할경우 INADDR_NONE('-1') 을반환 - -1 은 Dotted Decimal 로표현시 255.255.255.255 이므로유효성검사가반드시필요
<38> <Network Programming> Address Conversion Routines (2) int inet_aton (const char * strptr, struct in_addr * addrptr); - 'Dotted Decimal' 형태의문자열을바이너리형태로바꾸는함수 - strptr 로들어온 Dotted Decimal' 형태의문자열은변환되어 'in_addr' 타입의 addrptr 에저장됨 - 주소가올바르다면 0 이아닌값, 그렇지않다면 0 을반환 char * inet_ntoa (struct in_addr inaddr); - 'in_addr' 형의바이너리주소 inaddr 을 Dotted Decimal' 형태의문자열 (IP 주소 : 예 203.252.201.16) 로변경 - 반환되는문자열은정적으로할당된버퍼에저장 - 함수의연속적인호출은버퍼를중복하여덮어쓰므로주의 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> -inet_addr > inet_ntoa : 역변환 unsigned long inet_addr(char *ptr); char *inet_ntoa(struct in_addr inaddr);
<39> <Network Programming> 4. 원격지호스트정보를얻는함수 (1) 1. struct hostent * gethostbyname (const char * hostname); - 전달값으로호스트의이름 ( 도메인 ) 을받아서 hostent 구조체에결과값을돌려주는함수 2. struct hostent * gethostbyaddr (const char * addr, size_t len, int family); - 호스트의 IP 주소 ( 바이너리형태의주소 ) 를이용하여해당호스트에대한정보를저장 - addr 은호스트 IP 주소이고 len 은 IP 주소의크기 (IPv4 = 4, IPv6 = 16) 3. int gethostname (char * name, size_t namelen); - 현재의호스트이름을반환 - 전달인자 name 은호스트의이름을저장할곳의주소 - namelen 은 name 의바이트길이 - 성공한경우반환값 0, 실패인경우반환값 -1
<40> <Network Programming> 4. 원격지호스트정보를얻는함수 (2) 4. struct servent * getservbyname (const char * servname, const char * protoname); - 해당호스트에서진행되고있는서비스에대한각정보를서비스에대한이름과해당프로토콜로얻을수있게해주는함수 - 수행중에러발생시에는결과값으로 NULL 반환 5. struct servent * getservbyport (int port, const char * protoname); - 해당호스트에서진행되고있는서비스에대한각정보를포트번호로얻을수있게해주는함수 - 수행중에러발생시에는결과값으로 NULL 반환
<41> <Network Programming> IP 주소변환 ( 예제 ) ascii_ip.c 예제 struct in_addr host_ip; haddr = argv[1]; /* dotted decimal 주소 */ host_ip.s_addr = inet_addr(haddr); /* IP 주소 (hexa 4byte) 출력 */ printf( IP Address (hexa) 0x%x\n, host_ip.s_addr); /* dotted decimal 로다시변환하여출력 */ printf( %s\n, inet_ntoa(host_ip));
<42> <Network Programming> 예제 : IP 주소변환 (nslookup 기능 ) get_hostent.c 예제 struct in_addr host_ip; haddr = argv[1]; /* dotted decimal 주소 */ host_ip.s_addr = inet_addr(haddr); /* IP 주소 (hexa 4byte) 출력 */ printf( IP Address (hexa) 0x%x\n, host_ip.s_addr); /* dotted decimal 로다시변환하여출력 */ printf( %s\n, inet_ntoa(host_ip)); get_hostent.c 실행결과 #> get_hostent mm.sookmyung.ac.kr official host name : mm.sookmyung.ac.kr host address type : 2 length of host address : 4 IP address : 203.252.201.16 dotted decimal => 도메인네임 gethostbyaddr() 이용
<43> <Network Programming> 예제 : IP 주소변환 (nslookup 역기능 ) Get_hostname_ by_ipaddr: get_host_byaddr.c #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> int main(int argc, char *argv[]) { char *host; struct hostent *myhost; struct in_addr in; if(argc < 2) { printf(" 사용법 : %s IP 주소 (dotted decimal) \n", argv[0]); exit(0); } /* hostent 구하기 */ host = argv[1]; /* dotted decimal 주소 : IP address 로 String 형태 */ in.s_addr = inet_addr(host); /* 32bit 주소 : hexa decimal 로변환 */ myhost = gethostbyaddr((char *)&(in.s_addr), sizeof(in.s_addr), AF_INET); /* 원하는값 : 도메인이름 */ if (myhost == NULL) { printf("error at gethostbyaddr() \n"); exit(0); } /* 호스트이름출력 */ printf("official host name : %s\n", myhost->h_name); }
<44> <Network Programming> Homework Java API 의조사 java.sun.com 사이트내의 API 부분에서 http://java.sun.com/javase/6/docs/api Java.net InetAddress InetSocketAddress 정리
<45> <Network Programming> 네트워크프로그래밍을위한함수 1. 시스템호출함수의개요 2. 소켓관련함수 3. 소켓옵션관련함수 4. 동시처리를위한함수
<46> <Network Programming> 1. 시스템호출함수의개요 - 시스템콜이란개발의편의를위해운영체제의고유기능을호출하는것 - 동작매커니즘 1. 필요에의해시스템함수호출 2. 제어권한이동 ( 어플리케이션 > 시스템호출인터페이스 > 운영체제 ) 3. 운영체제내부모듈이호출에대한처리를진행 4. 제어권한이동 ( 운영체제 > 시스템호출인터페이스 > 어플리케이션 )
<47> <Network Programming> 2. 소켓관련함수 (1/8) 1. 소켓함수를이용한클라이언트 / 서버의구조
<48> <Network Programming> o socket system call 2. 소켓관련함수 (2/8) - 원하는통신 protocol(internet TCP, Internet UDP, XNS SPP 등 ) 의 type 을지정 o Syntax int socket (int family, int type, int protocol); #include <sys/types.h> #include <sys/socket.h> o socket system call 1. 네트워크통신을위해사용될새로운소켓생성 2. 파일기술자테이블에새로생성된소켓의인덱스를파일기술자번호로변환 return value : sock 지정번호, sockfd 3. 입력인자 family : 프로토콜의부류입력 AF_UNIX AF_INET AF_NS AF_IMPLINK UNIX internal protocols Internet protocols Xerox NS protocols IMP link layer type : 소켓과함께사용할통신형태입력 protocol : TCP/UDP/RAW 입력, 일반적으로 0 int socket (int family, int type, int protocol); o socket type -SOCK_STREAM -SOCK_DGRAM -SOCK_RAW -SOCK_SEQPACKET -SOCK_RDM stream socket datagram socket raw socket sequenced packet socket reliably delivered message socket (not implemented yet)
<49> <Network Programming> Elementary Socket System call o socket family 와 type 에대응하는 protocol AF_UNIX AF_INET AF_NS SOCK_STREAM Yes TCP SPP SOCK_DGRAM Yes UDP IDP SOCK_RAW IP Yes SOCK_SEQPACKET SPP o family, type 그리고 protocol 간의조합 family type protocol Actual protocol AF_INET AF_INET AF_INET AF_INET SOCK_DGRAM SOCK_STREAM SOCK_RAW SOCK_RAW IPPROTO_UDP IPPROTO_TCP IPPROTO_ICMP IPPROTO_RAW UDP TCP ICMP (raw) AF_NS AF_NS AF_NS AF_NS SOCK_STREAM SOCK_SEQPACKET SOCK_RAW SOCK_RAW NSPROTO_SPP NSPROTO_SPP NSPROTO_ERROR NSPROTO_RAW SPP SPP Error protocol (raw) IPPROTO_xxx - <netinet/in.h> NSPROTO_xxx - <netns/ns.h>
<50> <Network Programming> Elementary Socket System call( 계속 ) o socket system calls 과연계요소들 connection-oriented server connection-oriented client protocol local-addr, local-process foreign-addr, foreign-process socket() bind() listen(), accept() socket() connect() connectionless server scoket() bind() recvfrom() connectionless client socket() bind() sendto() o socketpair system call - 이름없이연결되어있는 sockvec[0] 과 sockvec[1] 이라는두가지 socket 지정번호를돌려줌 #include <sys/types.h> #include <sys/socket.h> int socketpair (int family, int type, int protocol, int sockvec[2]);
<51> <Network Programming> 2. 소켓관련함수 (3/) o bind system call - 이름없는 socket 에이름을부여 - 로컬인터페이스어드레스와포트번호를소켓과서로연관되게묶는함수입니다 o Syntax int bind (int sockfd, struct sockaddr * myaddr, int addrlen); #include <sys/types.h> #include <sys/socket.h> o int bind (int sockfd, struct sockaddr *myaddr, int addrlen); bind 의용도 1. 서버측에서처음생성된소켓이어떤주소값도갖고있지않음 2. server 는주지된주소를시스템에기록한다. 소켓에지역주소및포트 (Well-Known Port) 를할당하여활성화 3. 입력인자 sockfd : socket() 함수호출로얻은소켓의기술자입력 myaddr : 로컬 IP 및포트에대한정보가담긴 sockaddr_in 주소입력 addrlen : sockaddr_in 의크기입력 4. client는자신을위한특정주소를기록할수있다. 5. connectionless client는시스템이자신에게유일한주소를부여하였음을확인할필요가있으며, 이로써상대측 server가응답을보낼유효한반송주소를갖게되는것이다.
<52> <Network Programming> Bind 처음만들어질때소켓은그와관련된로컬어드레스를가지지않음. 소켓이 socket 함수를호출해서생성될때, 소켓은이름공간 (name space) 에존재합니다. 하지만, 할당된이름을가지지는않습니다. 그러므로, 이름지여지지않은소켓과로컬어드레스를연결지우기위해서이함수는사용 bind 함수는 connect 나 listen 함수를뒤이어호출하기전에, 접속되지않은소켓에사용 TCP 와 UDP 소켓모두사용 이함수에의해서할당되어지는이름은세가지부분으로구성 주소체계 (address family), 호스트주소 (host address), 어플리케이션에사용되는포트번호 (port number))
<53> <Network Programming> 2. 소켓관련함수 (4/8) o listen system call - connection을받아들이겠다는의지를나타내기위하여 connection-oriented server가사용 - socket과 bind system call 후와 accept system call전에수행 o Syntax - int listen (int sockfd, int backlog); #include <sys/types.h> #include <sys/socket.h> int listen (int sockfd, int backlog); 1. 서버측의접속대기큐의최대연결가능수설정 2. 입력인자 1. sockfd : socket() 함수호출로획득한소켓의소켓기술자입력 2. backlog : 접속대기큐의최대연결가능수를지정 (TCP 서버에서만사용 )
<54> <Network Programming> 2. 소켓관련함수 (5/8) o accept system call - connection-oriented server 가 listen system call 을수행한후, server 가 accept system call 을수행함으로써어떤 client process 로부터실제적 connection 을기다린다. o Syntax int accept (int sockfd, struct sockaddr * cliaddr, socklen_t * addrlen); #include <sys/types.h> #include <sys/socket.h> int accept (int sockfd, struct sockaddr *peer, int *addrlen); 1. TCP 서버에서호출되고연결이완료된큐의최초에위치한데이터반환 2. 큐가비어있는상태에는클라이언트의접속이이루어질때까지블록됨 3. 정상적으로함수가종료되면새로연결된파일기술자를반환 4. 입력인자 sockfd : listen() 함수호출로설정한소켓의소켓기술자입력 cliaddr : 클라이언트의 IP 주소와로컬포트정도를설정한구조체입력 addrlen : sockaddr 구조체의크기입력
<55> <Network Programming> 2. 소켓관련함수 (6/8) o connect system call - local system 과외부 system 사이의실제적인 connection 을설정 o Syntax int connect (int sockfd, const struct sockaddr * servaddr, socklen_t addrlen); #include <sys/types.h> #include <sys/socket.h> int connect (int sockfd, struct sockaddr *servaddr, int addrlen); 1. 클라이언트에서서버로의연결을담당 2. 소켓기술자와상대서버에대한주소정보를바탕으로서버로접속시도 3. 클라이언트에서 connect() 호출전에 bind() 를호출하지않았다고해도임의의포트와로컬 IP 를자동으로설정하여함수호출 4. 3-way handshaking 으로서버와연결을설정함 5. 입력인자 sockfd : socket() 함수호출로저장된소켓의소켓기술자입력 servaddr : 접속할서버의주소와포트에관한정보를담은구조체입력 addrlen : sockaddr 구조체의크기입력
<56> <Network Programming> 2. 소켓관련함수 -TCP(7/8) read / write / recv / send 1. ssize_t read (int sockfd, void * buffer, size_t len); - 원하는소켓 (sockfd) 에서특정길이 (len) 만큼을사용자버퍼 (buffer) 로읽어들임 2. ssize_t write (int sockfd, void * buffer, size_t len); - 원하는소켓 (sockfd) 에서특정길이 (len) 만큼을사용자버퍼 (buffer) 로보냄 3. ssize_t recv (int sockfd, void * buffer, size_t len, int flags); - read 함수와동일하나 flags 변수의값에따라데이터수신방법이다름 - flags 옵션 MSG_PEEK : 네트워크버퍼에서데이터제거를하지않고버퍼에복사만함 MSG_OOB : 긴급메시지전달에사용하며 TCP 헤더의 URG bit을 1로변경 MSG_WAITALL : 완전한양의데이터를수신할때만 recv 호출에서복귀 4. ssize_t send (int sockfd, void * buffer, size_t len, int flags); - Write 함수와동일하나 flags 변수의값에따라데이터송신방법이다름 - flags 옵션 MSG_OOB : OOB Data 송신 MSG_DONTROUTE : 데이터를라우팅하지않는다. 즉라우팅조건을무시
<57> <Network Programming> 2. 소켓관련함수 -UDP(8/8) sendto / recvfrom ssize_t recvfrom (int sockfd, void * message, size_t len, int flags, struct sockaddr * send_addr, size_t * add_len); ssize_t sendto (int sockfd, void * message, size_t len, int flags, const struct sockaddr * dest_addr, size_t dest_len); - 두함수도데이터송수신을담당하지만 recvfrom( 수신 ) 과 sendto( 송신 ) 함수는 UDP 통신에서사용 - recv/send와의차이점은목적지에대한정보가추가된다는것으로이는 UDP의특징인비연결성을생각하면송수신에반드시필요한정보임을알수있음 close / shutdown int close (int sockfd); - 소켓종료후에는이전에연결된상대방에게데이터송신불가 - 대기열의데이터에대해서해당데이터수신작업완료후소켓종료 int shutdown (int sockfd, int howto); - TCP 연결에대한종료를담당하고옵션에따라종료방법조절가능 - close() 함수와다르게별다른작업없이바로종료시킨다는차이 - howto 인자에따른옵션 SHUT_RD : Read 불가, Write 가능 SHUT_WR : Read 가능, Write 불가 SHUT_RDWR : Read / Write 모두불가
<58> <Network Programming> 3. 소켓옵션관련함수 int getsockopt (int sockfd, int level, int optname, void * optval, socklen_t * optlen); - 소켓 (sockfd) 의지정된옵션 (optname) 의값 (optval) 과바이트수 (optlen) 을반환 int setsockopt (int sockfd, int level, int optname, void * optval, socklen_t optlen); - 소켓 (sockfd) 에옵션 (optname) 을지정하고그에따른값 (optval) 과바이트수 (optlen) 을설정 소켓및프로토콜종류와그에따른옵션, 데이터값으로구성 level : 소켓및프로토콜의종류 (IP, ICMP, IPv6, TCP 등 ) optname : 세부옵션 optval : 선택한옵션에대한데이터값 optlen : optval 의크기입력
<59> <Network Programming> 2. 옵션의주요내용 3. 소켓옵션관련함수
<60> <Network Programming> 4. 다중처리를위한함수 (1/4) fork() 함수 1. 새로운프로세스를생성하고복제된프로세스는현재프로세스와같은속성과코드를소유 2. 부모프로세스는자식프로세스에대한개수에대한제한이있으므로에러발생에대한제어필요 3. 형태 pid_t fork (void); 4. 동작 5. 특징 코드, 스택,, 파일기술자, 소켓번호등을공유 PID 와변수는공유하지않음
<61> <Network Programming> 4. 다중처리를위한함수 (2/4) select () 함수 1. 파일기술자의상태변화감지후해당상태에대한처리 2. 상태변화는읽기 / 쓰기 / 예외처리의 3가지 3. 형태 - int select ( int n, fd_set * readfds, fd_set * writefds, fd_set *exceptfds, const struct timeval * timeout); 4. 입력인자 n : 상태를살펴볼최대기술자크기 timeout : 상태를살펴볼주기설정 (0인경우무한반복 ) readfds / writefds / exceptfds : 읽기 / 쓰기 / 예외처리관련기술자 5. 상태기술자관리를위한매크로들
<62> <Network Programming> select() 시스템콜 int select ( int maxfdp1, /* 최대소켓번호크기 + 1 */ fd_set *readfds, /* 읽기변화를감지할소켓번호 */ fd_set *writefds, /* 쓰기변화를감지할소켓번호 */ fd_set *exceptfds, /* 예외변화를감지할소켓번호 */ struct timeval *tvptr); /* select 시스템콜이기다리는시간 */
<63> <Network Programming> select() 시스템콜 (Cont d) 매크로 FD_ZERO(fd_set *fdset) fdset 의모든비트를지운다 FD_SET(int fd, fd_set *fdset) fdset 중소켓 fd 에해당하는비트를 1 로한다 FD_CLR(int fd, fd_set *fdset) fdset 중소켓 fd 에해당하는비트를 0 으로한다 FD_ISSET(int fd, fd_set *fdset) fdset 중소켓 fd 에해당하는비트가세트되어있으면양수값을리턴
<64> <Network Programming> Select 를이용한예
<65> <Network Programming> 4. 다중처리를위한함수 (3/4) poll() 함수 1. select 함수와같은기술자관리방식 2. select 에비하여관리할기술자를선택할수있음 3. select 함수보다빠른응답속도를보이나사용이불편한단점 4. 형태 - Int poll (struct pollfd *ufds, unsigned int nfds, int timeout); 5. 입력인자
<66> <Network Programming> 4. 다중처리를위한함수 (4/4) Thread 1. 스레드는프로세스와는다르게메모리공유가능 2. 메모리공유로인하여동기화문제발생 ( 관련도서참고 ) 3. 사용함수 Int pthread_create (pthread_t * thread, pthread_attr_t * attr, void * (* start_routine)(void *), void * arg); void pthread_join (pthread_t th, void ** thread_return); fork 에서자식프로세스를기다리는 wait 함수와같은기능 void pthread_exit (void *retval); 스레드를종료시킬때호출
<67> <Network Programming> 다중처리를위한 Server 의동작예제 (1) o concurrent server 로가정할경우의전형적인시나리오 int sockfd, newsockfd; if ( (sockfd = socket(...)) < 0) err_sys("socket error"); if (bind(sockfd,...) < 0) err_sys("bind error"); if (listen(sockfd, 5) < <0) err_sys("listen error"); for ( ; ; ) { newsockfd = accept(sockfd,...); /* blocks */ if (newsockfd < 0) err_sys("accept error"); if (fork() == 0) { close(sockfd); /* child */ doit(newsockfd); /* process the request */ exit(0); } } close(newsockfd); /* parent */
<68> <Network Programming> 다중처리를위한 Server 의동작예제 (2) o iterative server 로가정할경우의전형적인시나리오 int sockfd, newsockfd; if ( (sockfd = soket(...)) < 0) err_sys("socket error"); if (bind(sockfd,...) < 0) err_sys("bind error"); if (listen(sockfd, 5) < <0) err_sys("listen error"); for ( ; ; ) { newsockfd = accept(sockfd,...); /* blocks */ /* No Fork of Process */ if (newsockfd < 0) err_sys("accept error"); } doit(newsockfd); /* process the request */ close(newsockfd);
<69> <Network Programming> 클라이언트 / 서버의작성방법 1. 서버의유형 2. 연결형클라이언트 / 서버의작성 3. 비연결형클라이언트 / 서버의작성
<70> <Network Programming> 서버프로그램작성절차 서버프로그램이먼저수행되고있어야한다. 서버는 socket() 을호출하여통신에사용할소켓을개설한다. 소켓번호와자신의소켓주소를 bind() 로서로연결한다. 소켓주소 ( 자신의 IP 주소자신의포트번호 ) 소켓번호는응용프로그램이알고있는통신창구번호이고, 소켓주소는네트워크시스템 ( 즉, TCP/IP) 이알고있는주소이므로이들의관계를묶어두어야한다. listen() 을호출하여클라이언트로부터의연결요청을기다리는수동대기모드로들어간다. 클라이언트로부터연결요청이왔을때이를처리하기위하여 accept() 를호출한다. 클라이언트가 connect() 를호출하여연결요청을해오면이를처리한다. 메시지를송수신한다.
<71> <Network Programming> 클라이언트프로그램작성절차 socket() 을호출하여소켓을만든다. 서버에게연결요청을보내기위하여 connect() 를호출한다. 이때서버의소켓주소 (4+5) 구조체를만들어인자로준다. 소켓주소 ( 상대방의 IP 주소상대방의포트번호 ) 상대방 == 서버의 IP 주소및포트번호 대부분의클라이언트는 bind() 를사용하여포트번호를특정한값으로지정할필요가없다 ( 클라이언트는보통시스템이자동으로배정하는포트번호를사용한다 ). connect() 를호출하여연결요청을한다. 메시지를송수신한다.
<72> <Network Programming> 소켓의동작모드 blocking 모드 함수를호출했을경우동작완료때까지멈춤 listen(), connect(), accept(), read(), write() 등 non-blocking 모드 시스템콜호출후바로값이리턴 계속루프 (loop) 를돌면서확인 ( 폴링 ) 비동기 (asynchronous) 모드 IO 변화를감지 select() 함수를사용
<73> <Network Programming> 1. 서버의유형 1. 서버유형의결정 1. 클라이언트접속빈도 - 얼마만큼의클라이언트가어느정도의빈도로접속하는지에따름 2. 필요한규모와처리량 - 주고받는데이터의크기가어느정도이며필요한대역폭에따름 3. 데이터의안정성 - 클라이언트간혹은서버와클라이언트간의데이터신뢰도 - 요즘의네트워크망은설비가잘되어있어안정성에대한문제는줄어듬 2. 기본적인서버의유형 서버타입선택사항클라이언트의요청에대한처리방식동시처리형단순형서버로의접근방식연결형 (TCP) 비연결형 (UDP)
<74> <Network Programming> 연결형클라이언트 socket() 호출시소켓번호와소켓인터페이스의관계
<75> <Network Programming> 연결형서버 P/G Iterative 모델의 TCP 서버프로그램작성절차
<76> <Network Programming> 비연결형클라이언트 SOCK_DGRAM 으로소켓생성 connect() 없이바로메시지송수신 각패킷마다 IP 주소와포트번호를입력 sendto(int s, char* buf, int length, int flag, sockaddr* to, int tolen) recvfrom(int s, char* buf, int length, int flags, sockaddr* from, int fromlen)
<77> <Network Programming> 포트번호배정 클라이언트포트번호배정시기 TCP 소켓의경우 connect() 호출이후에 UDP 소켓의경우 sendto() 호출이후에 getsockname() 을이용확인 getsockname(s, (struct sockaddr *)&clinet_addr, &addr_len); port = clinet_addr.sin_port; printf( client port number : %d\n, port);
<78> <Network Programming> 2. 연결형클라이언트 / 서버의작성 1. 단순연결형클라이언트 / 서버 1) 서버와클라이언트의소켓생성 2) 서버는 BIND 작업후대기큐설정 (LISTEN) 3) 서버는클라이언트의접속대기 (ACCEPT) 4) 클라이언트가서버로접속시도 (CONNECT) 5) 접속이완료되면서버와클라이언트간의데이터송수신작업 6) 서버종료작업 7) 서버와클라이언트소켓닫음 (CLOSE)
<79> <Network Programming> ( 예 ) 단순연결형서버 socket 생성및수행 /* 서버소켓생성 */ server_socket = socket(af_inet, SOCK_STREAM, 0); /* 서버주소및포트설정 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(server_port); server_addr.sin_addr.s_addr = htonl(inaddr_any); memset(&(server_addr.sin_zero), 0, 8); /* 해당주소및포트로바인딩작업 */ bind(server_socket, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)); /* 클라이언트의대기열수설정 */ listen(server_socket, 10); client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &addrsize); msgsize = read(client_socket, BUFF, sizeof(buff)); if (msgsize <= 0) { printf( message receive error\n ); continue; } write(client_socket, BUFF, strlen(buff) );
<80> <Network Programming> ( 예 ) 단순 Client socket 생성및수행 /* 소켓생성 TCP 이므로 SOCK_STREAM*/ connect_fd = socket(af_inet, SOCK_STREAM, 0); /* 사용자의서버 IP 및포트에대한입력을받아서 sockaddr_in 구조체를구성 */ connect_addr.sin_family = AF_INET; connect_addr.sin_port = htons(atoi(argv[2])); connect_addr.sin_addr.s_addr = inet_addr(argv[1]); memset(&(connect_addr.sin_zero), 0, 8); /* 서버로의소켓접속 */ connect(connect_fd, (struct sockaddr*)&connect_addr, sizeof(connect_addr)); memset(msg, '\0', sizeof(msg)); fgets(msg, 1024, stdin); msgsize = strlen(msg); write(connect_fd, msg, msgsize); msgsize = read(connect_fd, msg, sizeof(msg)); if (msgsize <= 0) continue; printf("server Message>>%s", msg);
<81> <Network Programming> Homework 1. 앞의예제를이용하여단방향전달프로그램을작성하세요. ( 일종의 Echo 프로그램 ) ->../Soc/Sim_soc/sim_*.c 2. 위의예제를이용하여 Talk program 을작성하세요. ->../Soc/Talk/talk_[cs].c 3. TCP 기반 Talk Program 을 UDP 로변경하세요.
<82> <Network Programming> 2. 연결형클라이언트 / 서버의작성 2. 다중연결형클라이언트 / 서버 1) 서버와클라이언트의통신초기화 (SELECT 관련 ) 2) 서버와클라이언트소켓의생성 3) 서버는 BIND 작업후대기큐설정 (LISTEN) 4) 서버는클라이언트의접속대기 (ACCEPT) 5) 클라이언트가서버로접속시도 (CONNECT) 6) 서버측의기술자에이벤트가발생 7) 접속처리후클라이언트와데이터송수신 8) 서버종료작업 9) 서버와클라이언트소켓닫음 (CLOSE)
<83> <Network Programming> ( 예 ) 다중연결형서버 socket 생성및수행 /* 서버소켓생성 */ server_socket = Socket(AF_INET, SOCK_STREAM, 0); /* 서버주소및포트설정 */.. /* 해당주소및포트로바인딩작업 */ Bind(server_socket, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)); /* 클라이언트의대기열수설정 */ Listen(server_socket, 10); /*fd_set 데이타필드초기화 */ FD_ZERO(&readfd); maxfd = server_socket; client_index = 0; /* client 참가자수 : num_chat */ while(1) { /*fd_set 를해당소켓기술자로설정 */ FD_SET(server_socket, &readfd); /* 접속된클라이언트의소켓기술자를 fd_set 에설정 */ for (start_index = 0; start_index < client_index; start_index++) { FD_SET(client_fd[start_index], &readfd); if (client_fd[start_index] > maxfd) maxfd = client_fd[start_index]; } maxfd = maxfd + 1; return 0; 다음페이지계속
<84> <Network Programming> select(maxfd, &readfd, NULL, NULL,NULL); /* 해당소켓기술자에변화가생겼는지검사 */ if (FD_ISSET(server_socket, &readfd)) { addrsize = sizeof(struct sockaddr_in); client_socket = Accept(server_socket, (struct sockaddr*)&server_addr, &addrsize); FD_SET(client_socket, &readfd); client_fd[client_index] = client_socket; client_index++; if (client_index == 5) break; } /* 해당소켓기술자에변화가생겼는지검사서버에접속된모든클라이언트의소켓기술자검사 */ for (start_index = 0; start_index < client_index; start_index++) { if (FD_ISSET(client_fd[start_index], &readfd)) { msgsize = read(client_fd[start_index], BUFF, sizeof(buff)); if (msgsize <= 0) continue; printf("client Message>>%s", BUFF); msgsize = strlen(buff); write(client_fd[start_index], BUFF, msgsize); } }
<85> <Network Programming> ( 예 ) 다중 Client socket 생성및수행 /* 서버로의소켓접속 */ Connect(connect_fd, (struct sockaddr*)&connect_addr, sizeof(connect_addr)); /*fd_set 데이타필드초기화 */ FD_ZERO(&readfd); while(1) { } /*fd_set 를해당소켓기술자로설정 */ FD_SET(0, &readfd); FD_SET(connect_fd, &readfd); select (connect_fd+1, &readfd, NULL, NULL, NULL); /* 해당소켓기술자에변화가생겼는지검사 */ if (FD_ISSET(connect_fd, &readfd)) { msgsize = read(connect_fd, msg, sizeof(msg)); if (msgsize <= 0) continue; printf("server Message>>%s", msg); } /* 해당입력기술자에변화가생겼는지검사 */ if (FD_ISSET(0, &readfd)) { memset(msg, '\0', sizeof(msg)); fgets(msg, 1024, stdin); msgsize = strlen(msg); write(connect_fd, msg, msgsize); }
<86> <Network Programming> 3. 비연결형클라이언트 / 서버의작성 1. 단순비연결형클라이언트 / 서버 1) 서버와클라이언트의소켓생성 2) 서버는 BIND 작업후대기 3) 서버와클라이언트간의데이터송수신 4) 서버는단순히클라이언트로데이터전송 5) 서버종료작업 6) 서버소켓닫음 6) 클라이언트소켓닫음 * 양쪽모두상대방의주소를알고있어야지속적인데이터송수신이가능함을기억
<87> <Network Programming> ( 예 ) 단순비연결형서버 /*1. 서버소켓생성 */ server_socket = Socket(AF_INET, SOCK_DGRAM, 0); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(server_port); server_addr.sin_addr.s_addr = htonl(inaddr_any); memset(&(server_addr.sin_zero), 0, 8); bind(server_socket, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)); addrsize = sizeof(struct sockaddr_in); while(1) { memset(buff, '\0', sizeof(buff)); msgsize = recvfrom(server_socket, BUFF, sizeof(buff), 0, (struct sockaddr*)&client_addr, &addrsize); if (msgsize <= 0) continue; } printf("client Message>>%s", BUFF); msgsize = strlen(buff); sendto(server_socket, BUFF, msgsize, 0, (struct sockaddr*)&client_addr, addrsize);
<88> <Network Programming> ( 예 ) 단순비연결형클라이언트 memset(msg, '\0', sizeof(msg)); fgets(msg, 1024, stdin); msgsize = strlen(msg); /* 사용자입력데이타에대한처리. 서버전송 * sendto 함수이용 */ msgsize = sendto(connect_fd, msg, msgsize, 0, (struct sockaddr*)&connect_addr, addrsize); printf("message Send>>%s", msg); memset(temp_buff, '\0', sizeof(temp_buff)); /* 서버에서의송신데이타에대한처리 recvfrom함수이용 */ recvfrom((int)sockfd, temp_buff, sizeof(temp_buff), 0, (struct sockaddr*)&server_addr, &temp_addrsize); printf("message Receive>>%s", temp_buff);
<89> <Network Programming> 3. 비연결형클라이언트 / 서버의작성 2. 동시처리비연결형클라이언트 / 서버 1) 서버와클라이언트의소켓생성 2) 서버는 BIND 작업후대기 3) 클라이언트가서버로접속데이터송신 4) 서버는클라이언트의접속시마다클라이언트처리용스레드생성 5) 이후의클라이언트와의통신은각각의스레드가담당 6) 서버종료작업 7) 종료시스레드를통한클라이언트종료 8) 서버와클라이언트소켓닫음 (CLOSE)
<90> <Network Programming> ( 예 ) 다중처리용비연결형서버 while(1) { memset(buff, '\0', sizeof(buff)); /* 접속클라이언트에대한수신데이타처리 */ msgsize = recvfrom(server_socket, BUFF, sizeof(buff), 0, (struct sockaddr*)&client_addr, &addrsize); if (msgsize <= 0) continue; memcpy(&curr_addr, &client_addr, sizeof(struct sockaddr_in)); memset(curr_buff, '\0', sizeof(curr_buff)); memcpy(curr_buff, BUFF, strlen(buff)); /* 수신에대한데이타처리는 Thread로처리. 현재의예제에서는실용성이많지않으나실제에서는수신데이타에대한처리는많은작업이있을수있음 */ pthread_create(&thread, NULL, thread_process, (void *)server_socket); } void* thread_process(void * server_socket) { memset(temp_buff, '\0', sizeof(temp_buff)); memcpy(temp_buff, CURR_BUFF, strlen(curr_buff)); printf("client Message>>%s", temp_buff); /*Thread로처리되는클라이언트데이터송신 */ sendto((int)server_socket, temp_buff, strlen(temp_buff), 0, (struct sockaddr*)&temp_addr, sizeof(struct sockaddr)); }
<91> <Network Programming> ( 예 ) 다중처리용비연결형클라이언트 /* 데이타수신에대한 Thread 생성 */ pthread_create(&thread, NULL, thread_process, (void*)connect_fd); while(1) { memset(msg, '\0', sizeof(msg)); fgets(msg, 1024, stdin); msgsize = strlen(msg); /* 사용자입력데이타에대한처리. 서버전송 * sendto 함수이용 */ msgsize = sendto(connect_fd, msg, msgsize, 0, (struct sockaddr*)&connect_addr, addrsize); printf("message Send>>%s", msg); } void* thread_process(void * sockfd) { while(1) { memset(temp_buff, '\0', sizeof(temp_buff)); /* 서버에서의송신데이타에대한처리 recvfrom 함수이용 */ recvfrom((int)sockfd, temp_buff, sizeof(temp_buff), 0, (struct sockaddr*)&server_addr, &temp_addrsize); printf("message Receive>>%s", temp_buff); } }
<92> <Network Programming> 채팅을위한클라이언트 / 서버프로그램작성 1. 프로그램개요및구조 2. 채팅프로그램
<93> <Network Programming> 1. 프로그램개요및구조 (1) 서버프로그램구조 1) 서버에서사용하는구조체초기화 2) 서버소켓생성및클라이언트대기 3) select 를이용한접속상황관리 - 클라이언트가접속하면소켓기술자의상태가변화하여접속여부확인 4) 접속처리 - 접속한클라이언트가넘겨주는정보로서버의각구조체를업데이트함 5) 데이터송수신 - 클라이언트로받은데이터는검색하여같은채팅방안에접속한각각의다른 클라이언트에게전송 - 데이터수정작업필요 ( 이름첨가등 ) 6) 서버소켓닫기 - 종료하지않는다면 3) 으로복귀 7) 프로그램종료
<94> <Network Programming> 채팅서버구조
<95> <Network Programming> 채팅서버구조 (Cont d) 채팅서버와클라이언트와의연결관계
<96> <Network Programming> /* 채팅에서의방을 3 개로설정 */ struct room_node roomlist[3]; 서버 : 채팅방설정 /* 각방에대한방이름초기화 */ strcpy(roomlist[0].room_name,"room1"); strcpy(roomlist[2].room_name,"room3"); strcpy(roomlist[1].room_name,"room2"); /* 각방의접속사용사수를초기화 */ roomlist[0].user_count = 0; roomlist[1].user_count = 0; roomlist[2].user_count = 0; /* 서버소켓생성 */ server_socket = socket(af_inet, SOCK_STREAM, 0); /* 서버 IP 및포트설정 */ server_addr.sin_family = AF_INET; server_addr.sin_port = htons(server_port); server_addr.sin_addr.s_addr = htonl(inaddr_any); memset(&(server_addr.sin_zero), 0, 8); bind(server_socket, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)); listen(server_socket, 0); FD_ZERO(&readfd);
<97> <Network Programming> 서버 : 서버에접속한클라이언트소켓기술자확인 /* 서버에접속한클라이언트소켓기술자 [ 각방의유저구조체포함 ] 를 fd_set 에설정 */ FD_SET(server_socket, &readfd); for (room_index = 0; room_index < 3; room_index++) { for (user_index = 0; user_index < roomlist[room_index].user_count; user_index++) { tempsockfd = roomlist[room_index].user_list[user_index].user_sockfd; FD_SET(tempsockfd, &readfd); } } maxfd = maxfd + 1; if (tempsockfd > maxfd) maxfd = tempsockfd;
<98> <Network Programming> 서버 : 클라이언트접속처리 select(maxfd, &readfd, NULL, NULL,NULL); /* 서버로의접속이있는클라이언트에대한처리 */ if (FD_ISSET(server_socket, &readfd)) { addrsize = sizeof(struct sockaddr_in); client_socket = accept(server_socket, (struct sockaddr*)&server_addr, &addrsize); memset(buff, '\0', sizeof(buff)); msgsize = read(client_socket, BUFF, sizeof(buff)); } if (msgsize <=0) { printf("enter user Error\n"); continue; }
<99> <Network Programming> 서버 : 각방별로참가자확인및확약 printf("receive Message:%s\n", BUFF); /* 각방이 5 명으로제한되어있으므로해당유저인원체크 */ if (BUFF[0] == '1') { printf("login Room1 Count:%d\n", roomlist[0].user_count); if (roomlist[0].user_count == 5) { strcpy(buff, "User Count Overflow Error"); write(client_socket, BUFF, strlen(buff)); close(client_socket); continue; /* 인원초과 */ } roomlist[0].user_list[roomlist[0].user_count].user_sockfd = client_socket; roomlist[0].user_count++; strcpy(buff, "ConnectOK"); write(client_socket, BUFF, strlen(buff)); }
<100> <Network Programming> 서버 : 참가자에게수신정보전달 for (room_index = 0; room_index < 3; room_index++) { for (user_index = 0; user_index < roomlist[room_index].user_count; user_index++) { if (FD_ISSET(roomlist[room_index].user_list[user_index].user_sockfd,&readfd)){ memset(buff, '\0', sizeof(buff)); tempsockfd = roomlist[room_index].user_list[user_index].user_sockfd; msgsize = read(tempsockfd, BUFF,sizeof(BUFF)); if (msgsize <= 0) { if (user_index == roomlist[room_index].user_count) { close(roomlist[room_index].user_list[user_index].user_sockfd); roomlist[room_index].user_count--; } else if (user_index < roomlist[room_index].user_count) { close(roomlist[room_index].user_list[user_index].user_sockfd); for (temp_user_count = user_index; temp_user_count < roomlist[room_index].user_count; temp_user_count++) { roomlist[room_index].user_list[temp_user_count] = roomlist[room_index].user_list[temp_user_count+1]; roomlist[room_index].user_count--; } } else { printf("receive Message=>%s\n", BUFF, msgsize); for (temp_user_count = 0; temp_user_count < roomlist[room_index].user_count; temp_user_count++) { msgsize = strlen(buff); write(roomlist[room_index].user_list[temp_user_count].user_sockfd, BUFF,msgsize); } } } } }
<101> <Network Programming> 1. 프로그램개요및구조 (2) 클라이언트프로그램구조 1) 서버에전달할기본데이터입력 - 사용자의대화명및방번호 2) 서버접속을위한구조체설정 - 서버주소및포트정보입력 3) 소켓생성 4) 서버로접속시도및확인 - Connect 함수이용하여접속 - 접속처리결과를서버로부터받음 5) 데이터수신 - 수신데이터는수정하지않고바로화면에출력함 - 서버에서분류되어전송되었으므로클라이언트는따로처리할필요없음 6) 데이터송신 - 자신의대화명추가후서버로송신 7) 클라이언트프로그램소켓닫기 - 종료하지않는다면 5) 으로복귀 8) 프로그램종료
<102> <Network Programming> 클라이언트 : 채팅방요구준비 /* 사용자대화명입력처리 */ fgets(name,30, stdin); name[strlen(name)-1] = '\0'; while(1) { /* 접속방번호입력 */ printf("#enter Room Number(ex=>1 or 2 or 3)=>"); room_number = (char)fgetc(stdin); fgetc(stdin); if (room_number!= '1' && room_number!='2' && room_number!= '3') { printf("incorrect Room Number\n"); continue; } else break; } memset(msg, '\0', sizeof(msg)); msg[0] = room_number; for (temp_index = 0; temp_index < strlen(name); temp_index++) { msg[temp_index+1] = name[temp_index]; }
<103> <Network Programming> 클라이언트 : 채팅방요구 connect_fd = socket(af_inet, SOCK_STREAM, 0); connect_addr.sin_family = AF_INET; connect_addr.sin_port = htons(atoi(argv[2])); connect_addr.sin_addr.s_addr = inet_addr(argv[1]); memset(&(connect_addr.sin_zero), 0, 8); /* 서버접속 */ connect(connect_fd, (struct sockaddr*)&connect_addr, sizeof(connect_addr)); msgsize = strlen(msg); write(connect_fd, msg, msgsize); memset(msg, '\0', sizeof(msg)); /* 채팅서버에대한접속결과확인 */ read(connect_fd, msg, sizeof(msg)); if (!strcmp(msg, "ConnectOK")) { printf("*******server Connection Success********\n"); printf("*******start Chatting Program ********\n"); } if (!strcmp(msg, "User Count Overflow Error")) { printf("%s\n", msg); exit(1); }
<104> <Network Programming> 클라이언트 : 채팅 FD_ZERO(&readfd); while(1) { FD_SET(0, &readfd); FD_SET(connect_fd, &readfd); select (connect_fd+1, &readfd, NULL, NULL, NULL); } if (FD_ISSET(connect_fd, &readfd)) { memset(msg, '\0', sizeof(msg)); msgsize = read(connect_fd, msg, sizeof(msg)); if (msgsize <= 0) continue; printf("*from=>%s\n", msg); } if (FD_ISSET(0, &readfd)) { memset(msg, '\0', sizeof(msg)); fgets(msg, 1024, stdin); msg[strlen(msg)-1] = '['; strcat(msg, name); msg[strlen(msg)] = ']'; msgsize = strlen(msg); write(connect_fd, msg, msgsize); }