1 / 23

(분산 통신 실습) : 소켓( Sockets)

(분산 통신 실습) : 소켓( Sockets). 소켓 : 저수준 패킷 스트림 전송 유닉스 소켓 (multi.incheon.ac.kr 117.16.244.53 및 117.16.244.59 에서 프로그램 ) ( 실습 1) Hello 프로그램 helloserver.c helloclient.c 컴파일 & 실행 ( 실습 2) 일대일 채팅 talk 프로그램 talk_server.c talk_client.c 컴파일 & 실행

kyra-wilson
Download Presentation

(분산 통신 실습) : 소켓( Sockets)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. (분산 통신 실습) : 소켓(Sockets) • 소켓: 저수준 패킷 스트림 전송 • 유닉스 소켓 (multi.incheon.ac.kr 117.16.244.53 및 117.16.244.59 에서 프로그램) • (실습1) Hello 프로그램 helloserver.c helloclient.c 컴파일 & 실행 • (실습2) 일대일 채팅 talk 프로그램 talk_server.ctalk_client.c 컴파일 & 실행 • (실습3) 다대다 채팅 chat 프로그램 chat_server.c chat_client.c 컴파일 & 실행 • (과제) 다대다 채팅 프로그램 디버깅 (채팅 참가자가 exit해도 진행되도록) • 자바소켓 • (실습1) 시간(Time-of-Day) 서버 소스 코드 분석 • (실습2) 유닉스 inettime 클라이언트와 혼합해서 inettime 서비스를 실행해 보세요. • (과제) 자바 응용 채팅 프로그램 JavaChatServer.java와 JavaChatClient.java 및 JavaChatClient.html 자바 애플릿 채팅 프로그램을 자바응용 채팅 프로그램으로 수정하세요. • 윈도우 소켓(winsock) • (실습1) 윈도우 소켓 chat 프로그램으로 컴파일 & 실행 • (과제) 윈도우 소켓 talk 프로그램 유닉스 talk 프로그램의 winsock 버전을 작성하세요. • [Sockets 실습 과제] 유닉스 소켓/자바 소켓/윈도우 소켓 과제 중 택 1 • http://marvel.incheon.ac.kr/의 Information의 Unix의 Socket Programming 참조

  2. Socket Communication 운영체제

  3. 유닉스 소켓 통신:helloserver.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #define PORTNUM 5059 main(int argc, char *argv[]) { int serverFd, clientFd, clilen, childpid; struct sockaddr_in cli_addr, serv_addr; // Open a TCP socket (an Internet stream socket). if((serverFd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ printf("server: can't open stream socket"); return -1; } // Bind our local address so that the client can send to us. bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(PORTNUM); if(bind(serverFd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) printf("server: can't bind local address"); printf("Server is binded\n");

  4. 유닉스 소켓 통신:helloserver.c listen(serverFd, 5); for( ; ; ){ // Wait for a connection from a client process. // This is an example of a concurrent server. clilen = sizeof(cli_addr); clientFd = accept(serverFd, (struct sockaddr *) &cli_addr, &clilen); printf("Server called\n"); if((childpid = fork()) < 0){ printf("server: fork error"); exit(0); } else if(childpid == 0){ /* child process */ /* printf("serverFd = %d, clientFd = %d\n", serverFd, clientFd); */ /* process the request */ write(clientFd,"Hello!",7); close(clientFd); /* close original socket */ return -1; } close(clientFd); /* parent process */ } }

  5. 유닉스 소켓 통신:helloclient.c #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #define PORTNUM 5059 main(int argc, char *argv[]) { int clientFd; char str[10]; char* hostaddress; struct sockaddr_in serv_addr; struct hostent* hostStruct; struct in_addr* hostNode; if(argv[1] == NULL){ printf("Usage: inetclient hostname(or server IP)\n"); printf(" (Ex) inetclient multi.inchon.ac.kr(or 211.119.245.149)\n"); exit(1); }

  6. 유닉스 소켓 통신:helloclient.c hostStruct = gethostbyname(argv[1]); if(hostStruct == NULL) return(0); hostNode = (struct in_addr*) hostStruct->h_addr; hostaddress = inet_ntoa(*hostNode); printf("host name is %s, host address is %s\n", argv[1], hostaddress); // Fill in the structure "serv_addr" with the address of the // server that we want to connect with. bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = hostNode->s_addr; serv_addr.sin_port = htons(PORTNUM); // Open a TCP socket (an Internet stream soskcet). if((clientFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) printf("client: can't open stream socket"); // Connect to the server if(connect(clientFd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) printf("client: can't connect to server"); /* printf("clientFd = %d\n", clientFd); */ read(clientFd, str, 10); printf("%s\n", str); close(clientFd); exit(0); }

  7. 유닉스 소켓 통신: talk & chat • 인터넷 talk 프로그램 • netprog의 talk_server.c talk_client.c 소스코드 참조 • 실행 % talk_server 포트번호 % talk_client 서버호스트IP주소 포트번호 • 인터넷 chat 프로그램 • 하나의 프로세스가 채팅 참가 신청 처리와 클라이언트들의 통신 처리를 다 할 수 있도록 accept() 또는 read()에서 계속 기다리는 동기(synchronous, blocking) 모드 대신 select() 시스템 호출을 이용하여 소켓을 비동기(asynchronous, non-blocking) 모드로 이용 • netprog의 chat_server.c chat_client.c 소스 코드 참조 • 실행 % chat_server 포트번호 % chat_client 서버호스트IP주소 포트번호 유닉스 네트워크 프로그래밍

  8. select() • select() • 소켓에서 발생하는 I/O 변화를 기다리다가 지정된 /O 변화가 발생하면 리턴 됨 • 초기 소켓 s와 참가한 클라이언트들의 소켓의 I/O 변화 감지 • select() 시스템 호출 형식 int select ( int maxfdp1, /* chleo 파일(및 소켓) 번호 크기 +1 */ fd_set *readfds, /* 읽기 상태 변화를 감지할 소켓 지정 */ fd_set *writefds, /* 쓰기 상태 변화를 감지할 소켓 지정 */ fd_set *exceptfds, /* 예외 상태 변화를 감지할 소켓 지정 */ struct timeval *tvptr); /*select() 시스템 호출이 기다리는 시간 */ • FD_ZERO(fd_set *fdset) 매크로로 모든 비트 지움 • FD_SET(int fd, fd_set *fdset) 매크로로 소켓 fd의 fdset 변화를 감지하기 위해 fdset 중 fd에 해당하는 비트를 1로 지정 • FD_ISSET(int fd, fd_set *fdset) 매크로는 fdset의 해당 fd 비트가 1로 지정되어 있으면 양수값 리턴하므로 fd에게 fdset 변화가 발생했는지 판단 유닉스 네트워크 프로그래밍

  9. Time-of-Day: Server.java import java.net.*; import java.io.*; public class DateServer { public static void main(String[] args) { try { ServerSocket sock = new ServerSocket(6013); // now listen for connections while (true) { Socket client = sock.accept(); // we have a connection PrintWriter pout = new PrintWriter(client.getOutputStream(), true); // write the Date to the socket pout.println(new java.util.Date().toString()); // close the socket and resume listening for more connections client.close(); } } catch (IOException ioe) { System.err.println(ioe); } } }

  10. Time-of-Day: Client.java import java.net.*; import java.io.*; public class DateClient { public static void main(String[] args) { try { // this could be changed to an IP name or address other than the localhost Socket sock = new Socket("127.0.0.1",6013); InputStream in = sock.getInputStream(); BufferedReader bin = new BufferedReader(new InputStreamReader(in)); String line; while( (line = bin.readLine()) != null) System.out.println(line); sock.close(); } catch (IOException ioe) { System.err.println(ioe); } } }

  11. EchoServer.java import java.net.*; import java.io.*; public class EchoServer { public static final int DEFAULT_PORT = 6007; public static final int BUFFER_SIZE = 256; public static void main(String[] args) throws IOException { ServerSocket sock = null; byte[] buffer = new byte[BUFFER_SIZE]; InputStream fromClient = null; OutputStream toClient = null; try { // establish the socket sock = new ServerSocket(DEFAULT_PORT); while (true) { /** * now listen for connections */ Socket client = sock.accept(); /** * get the input and output streams associated with the socket. */ fromClient = new BufferedInputStream(client.getInputStream()); toClient = new BufferedOutputStream(client.getOutputStream()); int numBytes; /** continually loop until the client closes the connection */ while ( (numBytes = fromClient.read(buffer)) != -1) { toClient.write(buffer,0,numBytes); toClient.flush(); } fromClient.close(); toClient.close(); client.close(); } } catch (IOException ioe) { } finally { if (sock != null) sock.close(); } } }

  12. talk & chat Winsock versions • 윈도우 소켓(Window socket)은 유닉스에서 사용되는 BSD소켓을 계승하기 때문에 윈속에서 사용되는 대부분의 함수와 구조체는 유닉스 버전과 동일하다. 그러나 윈속을 사용하기 전에 유닉스와 달리 윈속 라이브러리를 초기화 해주고 사용이 끝나면 해제를 시켜줘야 한다. • 초기화 : WSAStartup, 해제 : WSACleanup • talk_client, talk_server • 유닉스 버전 : 키보드 입력과 데이터 수신 처리를 fork를 이용해서 분기 • 윈도우 버전 : 키보드 입력은 메인에서, 데이터수신 처리는 쓰레드를 이용 • chat_client, chat_server • 유닉스 버전 : select를 사용하여 데이터 I/O를 비동기적으로 처리, 키보드 입력은 fork를 이용 • 윈도우 버전 : I/O방식은 쓰레드에서 select를 사용, 키보드 입력은 메인에서 처리

  13. chat_server.c /*---------------------------------------------------------------------------------------------- 파일명: chat_server.c 기 능: 채팅참가자관리, 채팅메시지수신및방송 컴파일: VC++ 6.0 LIB : ws2_32.lib 실행예: chat_server 4001 작성일: 2003.03.06 made by JAXAL -----------------------------------------------------------------------------------------------*/ #include<stdio.h> #include<winsock2.h> #define MAXLINE 1024 #define MAX_SOCK 512 char *escapechar = "exit\n"; /* ------------------------------- 종료문자확인함수---------------------------- exitCheck()는다음의세개의인자를필요로한다 rline: 클라이언트가전송한문자열포인터 escapechar: 종료문자포인터 len: 종료문자의크기 ---------------------------------------------------------------------------------------------*/

  14. chat_server.c int exitCheck(char *rline, char *escapechar, int len) { int i, max; char *tmp; max = strlen(rline); tmp = rline; for(i = 0; i<max; i++) { if (*tmp == escapechar[0]) { if(strncmp(tmp, escapechar, len) == 0) return 1; } else tmp++; } return -1; }

  15. chat_server.c int main(int argc, char *argv[]) { char rline[MAXLINE]; char *start = "대화방에오신걸환영합니다...\n"; int i, j, n; SOCKET s, client_fd; int clilen, nfds; /* 최대소켓번호+1 */ fd_set read_fds; /* 읽기를감지할소켓번호구조체*/ int num_chat = 0; /* 채팅참가자수*/ /* 채팅에참가하는클라이언트들의소켓번호리스트*/ SOCKET client_s[MAX_SOCK]; struct sockaddr_in client_addr, server_addr; WSADATA wsaBuf; if(argc < 2) { printf("실행방법:%s 포트번호\n",argv[0]); return -1; } WSAStartup(MAKEWORD(2, 2), &wsaBuf); printf("대화방서버초기화중....\n"); /* 초기소켓생성*/ if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { printf("Server: Can't open stream socket."); return -1; }

  16. chat_server.c /* server_addr 구조체의내용세팅*/ memset((char *)&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(atoi(argv[1])); if (bind(s,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0) { printf("Server: Can't bind local address.\n"); return -1; } /* 클라이언트로부터연결요청을기다림*/ listen(s, 5); nfds = s + 1; /* 최대소켓번호+1 */ FD_ZERO(&read_fds); while(1) { /* (최대소켓번호+1) 값을갱신*/ if((num_chat-1) >= 0) nfds = client_s[num_chat-1] + 1; /* 읽기변화를감지할소켓번호를fd_set 구조체에지정*/ FD_SET(s, &read_fds); for(i=0; i<num_chat; i++) FD_SET(client_s[i], &read_fds);

  17. chat_server.c /*--------------------------------------- select() 호출----------------------------------------- */ if (select(0, &read_fds, (fd_set *)0, (fd_set *)0,(struct timeval *)0) == SOCKET_ERROR) { printf("select error\n"); return -1; } /*------------------------------ 클라이언트연결요청처리------------------------------- */ if(FD_ISSET(s, &read_fds)) { clilen = sizeof(client_addr); client_fd = accept(s, (struct sockaddr *)&client_addr, &clilen); if(client_fd != -1) { /* 채팅클라이언트목록에추가*/ client_s[num_chat] = client_fd; num_chat++; send(client_fd, start, strlen(start), 0); printf("%d번째사용자추가.\n",num_chat); } }

  18. chat_server.c /*------ 임의의클라이언트가보낸메시지를모든클라이언트에게방송----- */ for(i = 0; i < num_chat; i++) { if(FD_ISSET(client_s[i], &read_fds)) { if((n = recv(client_s[i], rline, MAXLINE,0)) > 0) { rline[n] = '\0'; /* 종료문자입력시채팅탈퇴처리*/ if (exitCheck(rline, escapechar, 5) == 1) { shutdown(client_s[i], 2); if(i != num_chat-1) client_s[i] = client_s[num_chat-1]; num_chat--; continue; } /* 모든채팅참가자에게메시지방송*/ for (j = 0; j < num_chat; j++) send(client_s[j], rline, n, 0); printf("%s\n", rline); } } } } closesocket(s); WSACleanup(); }

  19. chat_client.c /*----------------------------------------------------------------------------------------- 파일명: chat_client.c 기 능: 서버에접속한후키보드의입력을서버에전달하고, 서버로부터오는메시지를화면에출력한다. 컴파일: VC++ 6.0 컴파일옵션: /MT LIB : ws2_32.lib 실행예: chat_client 203.252.65.3 4001 사용자_ID 작성일: 2003.03.06 made by JAXAL -------------------------------------------------------------------------------------------*/ #include<stdio.h> #include<winsock2.h> #include<process.h> #define MAXLINE 1024 #define MAX_SOCK 512 char *escapechar = "exit\n"; SOCKET s; /* 서버와연결된소켓번호*/ struct Name { char n[20]; /* 대화방에서사용할이름*/ int len; /* 이름의크기*/ } name; unsigned __stdcall ReceiveThreadFunc(void* pArguments);

  20. chat_client.c int main(int argc, char *argv[]) { char line[MAXLINE], sendline[MAXLINE+1]; int size; unsigned int threadID; struct sockaddr_in server_addr; WSADATA wsaBuf; HANDLE hThread; if( argc < 4 ) { printf("실행방법: %s 호스트IP주소포트번호사용자이름\n", argv[0]); return -1; } WSAStartup(MAKEWORD(2, 2), &wsaBuf); /* 채팅참가자이름구조체초기화*/ sprintf(name.n, "[%s]", argv[3]); name.len = strlen(name.n); /* 소켓생성*/ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("Client : Can't open stream socket.\n"); return -1; }

  21. chat_client.c /* 채팅서버의소켓주소구조체server_addr 초기화*/ memset((char *)&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(argv[1]); server_addr.sin_port = htons(atoi(argv[2])); /* 연결요청*/ if(connect(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { printf("Client : Can't connect to server.\n"); return -1; } else { printf("접속에성공했습니다..\n"); } hThread = (HANDLE)_beginthreadex(NULL, 0, &ReceiveThreadFunc, (void*)s, 0, &threadID);

  22. chat_client.c /* ------------------ 키보드입력처리 ----------------------*/ while(1) { memset(sendline, 0, sizeof(sendline)); scanf("%s",sendline); size = strlen(sendline); sprintf(line, "%s %s", name.n, sendline); if(send(s, line, size + name.len + 1, 0) != (size+name.len+1)) { printf("Can't send message\n"); return -1; } if (size == 4 && strncmp(sendline, escapechar, 4) == 0) { break; } } CloseHandle(hThread); WSACleanup(); return 0; }

  23. chat_client.c unsigned __stdcall ReceiveThreadFunc(void* pArguments) { SOCKET s = (SOCKET)pArguments; fd_set read_fds; FD_ZERO(&read_fds); while(1) { /* --------------------------- selelct()호출------------------------------- */ FD_SET(s, &read_fds); if(select(0, &read_fds, NULL, NULL, NULL) == SOCKET_ERROR) { printf("select error\n"); return -1; } /*---------------- 서버로부터수신한메시지처리 ---------------*/ if (FD_ISSET(s, &read_fds)) { char recvline[MAXLINE]; int size; if ((size = recv(s, recvline, MAXLINE, 0)) != SOCKET_ERROR) { recvline[size] = '\0'; printf("%s \n", recvline); } } } /* end of while() */ closesocket(s); }

More Related