윈속을 위한 헤더 및 라이브러리 설정


winsock2.h 헤더 파일을 포함 한다.


ws2_32.lib 라이브러리를 링크 시킨다.


윈속 사용을 위한 라이브러리 초기화 및 해제



윈속 관련 라이브러리 초기화 함수


#include<winsock2.h>


int WSAStartup(

WORD wVersionRequested,

LPWSADATA lpWSAData

);

WORD wVersionRequested 에 버전에 맞는 16진수를 전달해야한다.

예 Version 3.4 이면 0x0403 식으로 주버전이 뒤로간다.

 WORD MAKEWORD(BYTE bLow,BYTE bHigh);

bLow에 주 버전 bHigh 에 부 버전을 넣으면 16진수로 반환된다.

윈속 관련 라이브러리 해제 함수


int WSACleanup(void);



윈속 프로그램 코드 Template


#include<winsock2.h>


............


int main(int argc, char **argv)

{

WSADATA wsaData;

...............

if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)

error_handling("WSAStartup() error!");

..................


WSACleanup()

retune 0;

}



윈속 기반의 소켓 관련 함수


SOCKET socket(int af, int type ,int protocol);

윈도우에서도 socket을 생성하면 리눅스와 마찬가지로 파일(소켓)핸들이 만들어져서 리턴된다. 

※파일(소켓) 핸들도 정수형 데이터이다.

int bind(SOCKET s,const struct sockaddr FAR * name, int namelen);


int listen(SOCKET s, int backlog);


SOCKET accept(SOCKET s, struct sockaddr FAR *addr,int FAR *addrlen);


int connect(SOCKET s, const sockaddr FAR * name, int namelen);




윈속 기반의 데이터 입출력 함수


데이터 출력함수 

#include<winsock2.h>


int send(SOCKET s, const char FAR *buf, int len, int flags);


데이터 입력함수

#include<winsock2,h>


int recv(SOCKET s, char FAR *buf, int len, int flags);


send,recv 함수는 리눅스에도 존재하는 함수로 윈도우에서는 리눅스의 read,write함수가 없기에 send,recv함수를 사용한다.




예제

서버

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<winsock2.h>


void ErrorHandling(char *message);


int main(int argc, char **argv)

{

WSADATA wsaData;

SOCKET hServSock;

SOCKET hClntSock;

SOCKADDR_IN servAddr;

SOCKADDR_IN clntAddr;

int szClntAddr;

char message[]="hello World\n";


if(argc!=2){

printf("Usage : %s <port>\n",argv[0]);

}


if(WSAStartup(MAKEWORD(2,2), &wsaData) !=0)

ErrorHandling("WSAStartup() error!");


hServSock=socket(PF_INET, SOCK_STREAM, 0); //서버 소켓 생성

if(hServSock==INVALID_SOCKET)

ErrorHandling("socket() error");


memset(&servAddr, 0 ,sizeof(servAddr));

servAddr.sin_family=AF_INET;

servAddr.sin_addr.s_addr=htonl(INADDR_ANY);

servAddr.sin_port=htons(atoi(argv[1]));


if(bind(hServSock, (SOCKADDR*) &servAddr,sizeof(servAddr))==SOCKET_ERROR)

ErrorHandling("bind() error");


if(listen(hServSock,5)==SOCKET_ERROR)

ErrorHandling("listen() error");                  //연결 요청 대기


szClntAddr=sizeof(clntAddr);

hClntSock=accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr); //연결요청수락

if(hClntSock==INVALID_SOCKET)

ErrorHandling("accept() error");


send(hClntSock, message, sizof(message), 0);


closesocket(hClntSock);

WASCleanup();


클라이언트


#include<stdio,h>

#include<stdlib.h>

#incldue<string.h>

#incldue<winsock2.h>


void ErrorHandling(char *message);


int main(int argc, char **argv)

{

WSADATA wsaData;

SOCKET hSocket;

char message[30];

int strLen;

SOCKADDR_IN servAddr;


if(argc!=3){

printf("Usage : %s <IP> <port> \n",argv[0]);

exit(1);

}


if(WSAStartup(MAKEWORD(2,2), &wsadata) !=0)

ErrorHandling(WSAStartup() error!");


hSocket=socket(PF_INET, SOCK_STREAM, 0);

if(hSocket==INVALID_SOCKET)

ErrorHandling("hSocketet() error");


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(hSocket,  (SOCKADDR*)&servAddr, sizeof(servAddr))==SOCKET_ERROR)

Erroerhandling(:connect() error!);


strLen=recv(hSocket, message, sizeof(message)-1,0);

if(strLen==-1)

ErrorHandling("read() error!");

message[strLen]=0;

printf("Message from server : %s \n", message);


closesocket(hSocket);

WSACleanup();

return 0;

}


void ErrorHandling(char *message)

{

fputs(message, stderr);

fputc('\n',stderr);

exit(1);

}


파일의 조작

저 수준 파일 입출력 (Low-Level File Access)


리눅스 혹은 윈도우즈 자체에서 제공해 주는 파일 입출력 함수를 사용하여 파일을 관리

(파일의 생성 및 삭제, 데이터 입력 및 출력) 하는것을 의미함.


리눅스에서는 모든 것을 파일로 관리한다.

-파일,소켓,표준 입력(키보드),표준 출력(모니터)

※흔히 표준 입출력 함수라 함은 ANSI 표준에서 제공하는 함수를 말한다.


파일에 파일 디스크립터를 할당해서 관리(파일 디스크립터는 정수)

※여기서 파일은 단순 파일 뿐아니라 소켓,표준 입출력을 포함한 의미이다.


 리눅스는 모든 것을 파일로 간주하기 때문에 파일 입.출력 함수로 소켓을 통한 데이터 전송 및 모니터나 키보드로 부터 데이터를 입.출력 또한 가능하다.

여기서 말하는 입출력함수는 표준입출력 함수가 아니라 리눅스에서 제공하는 입출력함수이다.



파일 디스크립터(File Descriptor)


파일을 관리하기 위해서 모든 파일(파일,소켓,표준 입력,표준 출력)에 파일 디스크립터를 할당 해 준다.


파일 디스크립터 

대상 

표준 입력 (Standard Input)

표준 출력 (Standard Output)

표준 에러 출력(Standard Error)


기본적으로  파일디스크립터는 정수형으로 이루어지면 0~2까지는 위 표처럼 기본적으로 할당 되어있다.


      Program                            Operating System

 




5





7






 <--------------
 





<--------------

 

파일 A


소켓 B


소켓 C


파일 D


만약 위처럼 요청에 따라 운영체제에서 소켓과 파일에 번호를 붙여 주면 프로그램에서 파일의 이름이아닌 넘버링된 숫자만으로 운영체제에 파일의 입력이나 출력등을 요청할수있다.

비유를 하자면 도서관에서 책에 번호를 붙여서 관리를 하는 것이라 보면된다.



File open 및 close


#include<fcntl.h>

#include<sys/types.h>

#include<sys/stat.h>


int open(const char *path, int flag);

open 함수의 const char *path 인자는 열려고하는 파일의 위치와 이름을  함께지니고 있는 문자열을 전달하고 

int flag는 파일을 열때의 모드를 설정하는것이다.

보면 함수의 리턴형이 int 형이다.

이것이 바로 파일 디스크립터 이다.

#include<unistd.h>


int close(int fildes);

닫고자하는 파일의 디스크립터를 인자로 전달하면 된다.


MODE 

의미 

O_CREAT 

 파일이 없으면 생성

O_TRUNC 

존재하던 데이터를 모두 삭제 ,다시 기록

O_RDONLY 

읽기 전용 모드로 파일을 오픈 

 O_WRONLY

쓰기 전용 모드로 파일을 오픈 

O_RDWR 

읽기와 쓰기 모두 가능한 모드로 파일을 오픈 

O_APPEND 

파일이 '추가모드'로 열린다. 파일 포인터가 열린 파일의 마지막 부분을 가리킨다.

 O_EXCL

 

O_NOCTTY 

 

 O_SYNC

 

 O_NOFOLLOW

 결로명이 심볼릭 링크라면,파일오픈이 안된다.

O_DIRECTORY 

경로명이 디렉토리가 아니면,파일오픈이 안된다. 



Data read & write


#include<unistd.h>


ssize_t write(int fildes, const void *buf, size_t nbytes);

ssize_t : signed int ,   size_t : unsigned int 

int fildes 는 데이터를 보낼 파일 디스크립터를 전달하면 된다.

void *buf 는 전달하고자 원하는 데이터의 배열정보

※배열 이름은 상수 포인터이다.

size_t nbytes 전달하고자 원하는 데이터의 크기

#include<unistd.h>


ssize_t read(int fildes, void *buf, size_t nbytes);

위와 같은데 size_t nbytes 는 읽어들일 데이터의 최대크기로 더 큰 데이터가 와도 여기서 지정한 크기까지만 읽어들인다.








서버 소켓 구현의 이해



Telephone

전화기 구입


전화번호 할당


케이블에 연결


수화기를 든다



<-------------------->


<-------------------->


<-------------------->


<-------------------->

 Server Socket


소켓 생성


IP주소 할당


연결 요청 대기 상태


연결 수락



서버를 전화받는 상황에 비유를 해보면 소켓을 전화기로 볼 수 있다.


일단 전화를 하려면 전화기가 필요하듯이 서버에서 데이터를 주고 받기위해서는 데이터를 주고받을 매개체인 소켓이 일단 필요하다. 


전화기에 전화번호가 있듯이 당연하게 IP주소가 할당되어져야하고 연결 요청 대기 상태로 되있게끔 해야하는데 말하자면 전화기에 케이블을 연결하는 과정이라고 생각하면 쉽다.


그뒤로 전화가 왔을때 받으면 된다.

서버는 클라이언트가 연결을 요청했을때 이를 수락하면 된다.


일반적인 서버프로그래밍은 이러한 큰 틀을 가지고 있다.



일단 소켓통신을 위한 함수들은 간단하게 살펴보자.


소켓 생성


#include<sys/types.h>

#include<sys/socket.h>


int socket(int domain, int type, int protocol)


IP 주소, Port 정보


#include<sys/socket.h>


int bind(int socket, struct sockaddr *myaddr, int addrlen)


연결 요청 대기 상태 진입


#include<sys/socket.h>


int listen(int sockfd, int backlog)


연결 요청 수락 


#include<sys/socket.h>


int accept(int sockfd, struct sockaddr *addr, int *addrlen)




클라이언트 소켓 구현의 이해


전화를 거는 사람도 전화기와 전화번호가 필요한 것 처럼 

서버 뿐만아니라 클라이어트도 소켓이 필요하다.


소켓 생성


위와 동일


연결요청


#include<sys/socket.h>


int connet(int socket, struct sockaddr *serv_addr, int addrlen)












네트워크의 이해

네트워크란 End-System 들을 연결하는 하나의 System을 의미한다.

End-System : "호스트"라 한다.

ex)PC,프린터,핸드폰 등등등



End-System 는 종단 시스템으로 쉽게 말하면 네트워크에 연결된 단말기. 

호스트 라한다. 네트워크에 모바일로 연결된 핸드폰,PDA도 호스트가 된다. 


Internet의 이해

멀리 떨어진 둘 이상의 네트워크가 연결되어 이뤄지는 거대한 네트워크를 의미한다.

인터넷의 구축을 위해서는 서로 다른 네트워크를 연결하는 장비가 필요한다.

이를 두고 라우터(Router)라 한다.




Client / Server 모델

Server와 Client는 프로그램이다.(서버 장비를 말하는 것이 아니다)

Server는 Client의 연결요청을 기다린다.

Client는 Server에 요청을 하고 응답을 기다리는 호스트를 의미한다.



Client는 Server에 요청을 하면 Server는 요청에 대해 응답을 해주고 Client는 이 응답을 기다리는 모델이다. 일반적으로 Server가 먼저 요청하는 경우는 없다.


Server의 종류

Server는 일반적으로 Client에 비해 복잡하다.

두 가지 종류의 서버

Iterative Server : 한 순간에 하나의 클라이언트에게 응답한다.

Concurrent Server : 동시에 여러 클라이언트에게 응답한다.

 




 




네트워크 프로그래밍의 이해

네트워크로 연결되어 있는 두 호스트간의 데이터 송수신.

파일 입출력과의 차이점은 데이터를 주고 받는 대상에 있다.

소켓(soket)이라는 장치를 사용하여 프로그래밍 한다.

소켓이란 원격에 존재하는 두 호스트를 연결시켜 주는 매개체 역할을 한다.





+ Recent posts