Upload
kyra-wilson
View
81
Download
0
Embed Size (px)
DESCRIPTION
(분산 통신 실습) : 소켓( 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 컴파일 & 실행 - PowerPoint PPT Presentation
Citation preview
1.1
( 분산 통신 실습 ) : 소켓 (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 컴파일 & 실행 ( 실습 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 참조
운영체제
Socket Communication
유닉스 소켓 통신 : 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");
1.3
유닉스 소켓 통신 : 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 */
}
}
1.4
유닉스 소켓 통신 : 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);
}
1.5
유닉스 소켓 통신 : 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);
}
1.6
유닉스 네트워크 프로그래밍 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 변화가 발생했는지 판단
1.9
Time-of-Day: Server.javaimport 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);
}
}
}
1.10
Time-of-Day: Client.javaimport 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);
}
}
}
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());
1.11
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();
}
}}
1.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 를 사용 , 키보드 입력은
메인에서 처리
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: 종료문자의크기
---------------------------------------------------------------------------------------------*/
1.13
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;
}
1.14
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;
}
1.15
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);
1.16
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);
}
}
1.17
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();
} 1.18
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);
1.19
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;
}
1.20
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);
1.21
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;
}
1.22
chat_client.cunsigned __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);
}1.23