Toggle menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

UnixSocketProgrammingAndWindowsImplementation: Difference between revisions

From ZeroWiki
imported>Unknown
No edit summary
 
(Repair batch-0003 pages from live compare)
 
(One intermediate revision by the same user not shown)
Line 64: Line 64:
     u_short sa_family;    /* address family */
     u_short sa_family;    /* address family */
   
   
     char sa_data[14];    /* 주소(IP 주소 + 포트 번호) */
     char sa_data[14];    /* 주소(IP 주소 + 포트 번호) */
   
   
  };
  };
Line 76: Line 76:
  struct in_addr sin_addr; // ip 주소
  struct in_addr sin_addr; // ip 주소
   
   
  char sin_zero[8]; // 쓰지 않는 주소
  char sin_zero[8]; // 쓰지 않는 주소
   
   
  };
  };
Line 133: Line 133:
  */
  */


=== sin_zero[[''''''8'''''']]: ===
=== sin_zero''''''8'''''': ===
// sin_zero 배열은 항상 0으로 채워져 있어야한다.
// sin_zero 배열은 항상 0으로 채워져 있어야한다.


Line 163: Line 163:


= Server 가 될 프로그램에 필요한 함수 =
= Server 가 될 프로그램에 필요한 함수 =
[[HTML(<img src="http://zeropage.org/pub/upload/sock.gif"><br>)]]
<img src="http://zeropage.org/pub/upload/sock.gif"><br>
== Bind - socket과 네트워크 정보를 연결하는 Bind!!! ==
== Bind - socket과 네트워크 정보를 연결하는 Bind!!! ==
  #include &lt;sys/socket.h&gt;
  #include &lt;sys/socket.h&gt;
Line 220: Line 220:


= Client 가 될 프로그램에 필요한 함수 =
= Client 가 될 프로그램에 필요한 함수 =
&#91;&#91;HTML(&lt;img src="http://zeropage.org/pub/upload/sock.gif"&gt;&lt;br&gt;)&#93;&#93;
<img src="http://zeropage.org/pub/upload/sock.gif"><br>
== connect - Server에 연결한다. ==
== connect - Server에 연결한다. ==
  ※ connect와 server 함수중 어떠한 함수가 닮았는지 이야기 해보자.
  ※ connect와 server 함수중 어떠한 함수가 닮았는지 이야기 해보자.
Line 253: Line 253:


= server/client 공통 - 입출력 함수 =
= server/client 공통 - 입출력 함수 =
&#91;&#91;HTML(&lt;img src="http://zeropage.org/pub/upload/sock.gif"&gt;&lt;br&gt;)&#93;&#93;
<img src="http://zeropage.org/pub/upload/sock.gif"><br>
== send/write - 상대에게 데이터를 보낸다. ==
== send/write - 상대에게 데이터를 보낸다. ==
  #include &lt;unistd.h&gt;
  #include &lt;unistd.h&gt;
Line 264: Line 264:
  예제 )
  예제 )
   
   
  char buf1[] = "Hello, World!";
  char buf1&#91;&#93; = "Hello, World!";
  char *buf2 = "Hello, World!";
  char *buf2 = "Hello, World!";
   
   
Line 287: Line 287:
  예제 )
  예제 )
   
   
  char buf1[200];
  char buf1&#91;200&#93;;
  char buf2[200];
  char buf2&#91;200&#93;;
  int str_len;
  int str_len;
   
   
Line 294: Line 294:
  str_len = read(sockfd, buf2, sizeof(buf2));
  str_len = read(sockfd, buf2, sizeof(buf2));
   
   
  buf1[str_len] = 0; // 배열의 끝을 설정해준다. 하지 않으면 뒤의 쓰레기 값까지 접근된다.
  buf1&#91;str_len&#93; = 0; // 배열의 끝을 설정해준다. 하지 않으면 뒤의 쓰레기 값까지 접근된다.
  buf1[str_len] = 0;
  buf1&#91;str_len&#93; = 0;
   
   
  // 타 시스템으로 이식을 위해 되도록 send를 사용하는 것이 좋다.
  // 타 시스템으로 이식을 위해 되도록 send를 사용하는 것이 좋다.
Line 381: Line 381:
   
   
  int sizeof_sockaddr_in;
  int sizeof_sockaddr_in;
  char msg[] = "Hello, World!";
  char msg&#91;&#93; = "Hello, World!";
   
   
  if( WSAStartup(MAKEWORD(2,2), &amp;wsaData) == -1 )
  if( WSAStartup(MAKEWORD(2,2), &amp;wsaData) == -1 )
Line 468: Line 468:
----
----
[[프로그래밍분류]]
[[프로그래밍분류]]

Latest revision as of 00:29, 27 March 2026

페이지의 컨텐츠를 보아하니, 따로 페이지를 뽑아내도 될것 같아 문서구조조정 하였습니다. 원래 페이지 이름은 데블스캠프2005/Socket Programming in Unix/Windows Implementation였습니다. - 임인택

주제 : Socket Programming의 기초적인 부분을 알아본다.

기본적인 함수/개념들

Socket

※ 소켓이란?
#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
// 성공 시 파일 디스크립터, 실패 시 -1 리턴

domain:

PF_INET : 인터넷 프로토콜 체계 사용

PF_INET6 IPv6 : 프로토콜 체계 사용

PF_UNIX : 유닉스 방식의 프로토콜 체계 사용 (프로세스간 통신)

PF_NS XEROX : 네트워크 시스템의 프로토콜 체계 사용

PF는 Protocol Family PF대신 AF를 사용해도 무방. (ex. PF_INET -> AF_INET)


type: 서비스 타입

※ TCP/IP, UDP란?

SOCK_STREAM : 스트림 방식의 소켓 생성 (TCP/IP)

SOCK_DGRAM : 데이터그램 방식의 소켓 생성 (UDP)

SOCK_RAW : raw 모드의 소켓 생성


protocol: 프로토콜

IPPROTO_TCP : TCP 기반. 값은 0이다.

IPPROTO_UDP : UDP 기반. 값은 0이다. // 우리가 사용하는 프로토콜인 TCP, UDP가 0이므로 0으로 써도 무방하다. // 구체적인 프로토콜을 선택할 때 사용하는데 대부분의 응용 프로그렘에서는 0으로 지정하면 된다.


예제)

main(){
int sockfd;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);

	if(sockfd == -1)
		fprintf(stderr, "socket 함수에서 에러"), exit(1);
	// 에러가 났을경우( sockfd == -1) 에러를 출력하고 프로그램 종료.
}



네트워크 정보

struct sockaddr {

    u_short sa_family;    /* address family */

    char sa_data[14];    /* 주소(IP 주소 + 포트 번호) */

};
struct sockaddr_in {

short sin_family;			// 주소 체계를 나타낸다.

u_short sin_port;			// port 번호

struct in_addr sin_addr;		// ip 주소

char sin_zero[8];			// 쓰지 않는 주소

};
※ 왜 sockaddr과 sockaddr_in의 structure가 같을까?


sin_family: // 주소체계

AF_INET : 인터넷 주소 체계

AF_UNIX : 유닉스 파일 주소 체계

AF_NS XEROX : 주소 체계

// sockaddr_in 은 TCP/IP체제 이므로 AF_INET만 사용한다. -> TCP/IP는 인터넷 기반이므로. // AF_INET/PF_INET -> 인터넷 프로토콜 체계 사용.

데이터를 Big-Endian으로 변환 시켜주는 체계.

unsigned short integer 변환 (2바이트 크기)
  htons(): host-to-network 바이트 변환 (Big-Endian으로 변환)
  ntohs(): network-to-host 바이트 변환 (해당 시스템)

unsigned long integer 변환 (4바이트 크기)
  htonl(): host-to-network 바이트 변환 (Big-Endian으로 변환)
  ntohl(): network-to-host 바이트 변환 (해당 시스템)

 ※ 왜 우리는 데이터를 Big-Endian으로 변환 시켜주어야할까?
 ※ 그렇다면 우리가 전송하는 데이터 모두 Big-Endian으로 변환 시켜주어야할까?


sin_port:

NULL : 임의의 포트를 할당한다. client에서 사용한다. // u_short sin_port 은 Big-Endian을 사용한다. // 따라서 Little_Endian을 사용하는 시스템에서는 Big-Endian으로 바꿔줘야한다.


sin_addr:

INADDR_ANY : 자기 자신의 주소를 할당한다. (== 0) // sin_addr은 인터넷 주소를 담고 있으므로 4 바이트가 필요하다. cf. 도메인 네임을 통한 연결은 설명하지 않겠습니다.

struct in_addr {
	unsigned long s_addr;
};
/*
	inet_addr(): 주소를 long형으로 계산하고 htonl()를 사용해 Big-Endian으로 변환 후 값을 return 한다.
		// 165.194.27.129 -> 165*256*256*256 + 194*256*256 + 27*256 + 129 = 2780961665
		// 2780961665 의 값은 Little-Endian 체계에서는 811BC2A5이다.
		// 이것을 A5C21B81로 바꿔 저장한다.


예제 )
	ina.sin_addr.s_addr = inet_addr("127.0.0.1");
*/

sin_zero'8':

// sin_zero 배열은 항상 0으로 채워져 있어야한다.

※ 왜 sin_zero가 만들어졌을까요?


예제)

#define PORT 9999
#define SERVER_IP "165.194.27.129"

main(){
struct sockaddr_in ina;

	memset((struct sockaddr *)&ina, 0, sizeof(struct sockaddr));
	// sin_zero를 0으로 채운다.
	// bzero라는 함수도 있지만 초기에 0으로 채우는 것이 편하다.
	// bzero(&(ina.sin_zero), 8);

	ina.sin_port = htons(PORT);			// PORT의 경우 정수를 넣어야한다.

	ina.sin_addr.s_addr = inet_addr(SERVER_IP);	// 클라이언트의 경우
							// SERVER_IP의 경우 문자열 포인터를 넣어야한다.
		// 165.194.27.129 -> 165*256*256*256 + 194*256*256 + 27*256 + 129 = 2780961665 로 저장이 된다.
	// ina.sin_addr.s_addr = INADDR_ANY;		// 서버의 경우
}


Server 가 될 프로그램에 필요한 함수

<img src="http://zeropage.org/pub/upload/sock.gif">

Bind - socket과 네트워크 정보를 연결하는 Bind!!!

#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *myaddr, int addrlen);
// 성공 시 0, 실패 시 -1 리턴
예제)

	if( bind(sockfd, (struct sockaddr *)&ina, sizeof(struct sockaddr) == -1 )
		fprintf(stderr, "bind에서 에러가 났습니다.n")), exit(1);


listen - client의 요청을 기다린다!

#include <sys/socket.h>

int listen(int sockfd, int backlog);
// 성공 시 0, 실패 시 -1 리턴

// backlog는 서버에 접속할 사람의 대기자 Maximum을 의미한다.
예제)

#deinfe BACKLOG 5		// 대기자가 5명이 넘으면 접속 불가능하다.

	if( listen(sockfd, BACKLOG) == -1 )
		fprintf(stderr, "listen에서 에러가 났습니다.n"), exit(1);


accpet - client의 요청을 받아들인다!

#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, int *addrlen);
// 성공 시 파일 디스크립터, 실패 시 -1 리턴
// *addrlen에 주의. accept는 client의 인터넷 정보가 들어오면 addrlen의 크기(struct sockaddr_in의 크기)와
// 비교를 하여 크다면 받아들이지 않고, 작다면 크기를 줄일것이다.

// child process를 생성해 다중 연결을 하는 것은 설명하지 않습니다.

예제 )

// int server_sock, client_sock
// struct sockaddr_in server_addr, client_sock

int sizeof_sockaddr_in;

	sizeof_sockaddr_in = sizeof(struct sockaddr_in);

	client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &sizeof_sockaddr_in);

	if( client_sock == -1 )
		fprintf(stderr, "accept에러. client가 서버에 접속 할 수 없습니다.");


Client 가 될 프로그램에 필요한 함수

<img src="http://zeropage.org/pub/upload/sock.gif">

connect - Server에 연결한다.

※ connect와 server 함수중 어떠한 함수가 닮았는지 이야기 해보자.
※ 이를 이야기 해보고 client의 프로그램의 네트워크 정보(struct sockaddr_in)에는 무엇이 들어가야하는지 이야기해보자.
#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
// 성공 시 0, 실패 시 -1 리턴
예제 )

	if( connect(client_sock, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) == -1 )
		fprintf(stderr, "서버에 connect 할 수 없습니다."), exit(1);


정리에 필요한 함수

close 파일을 닫는다.

#include <unistd.h>

int close(int fildes);

// 성공 시 0, 실패 시 -1 리턴
예제 )

	close(sockfd);


server/client 공통 - 입출력 함수

<img src="http://zeropage.org/pub/upload/sock.gif">

send/write - 상대에게 데이터를 보낸다.

#include <unistd.h>

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

// 성공 시 전달 한 바이트 수, 실패 시 -1 리턴
예제 )

char buf1[] = "Hello, World!";
char *buf2 = "Hello, World!";

	if( send(client_sock, buf1, sizeof(buf), 0) == -1 )
		fprintf(stderr, "send error");

	if( write(client_sock, buf2, strlen(buf2)) == -1 )
		fprintf(stderr, "write error");

// 타 시스템으로 이식을 위해 되도록 send를 사용하는 것이 좋다.


recv/read - 상대에게 데이터를 받는다.

#include <unistd.h>

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

// 성공 시 수신 한 바이트 수(단 EOF만나면 0), 실패 시 -1 리턴
예제 )

char buf1[200];
char buf2[200];
int str_len;

	str_len = recv(sockfd, buf1, sizeof(buf1), 0);
	str_len = read(sockfd, buf2, sizeof(buf2));

	buf1[str_len] = 0;			// 배열의 끝을 설정해준다. 하지 않으면 뒤의 쓰레기 값까지 접근된다.
	buf1[str_len] = 0;

// 타 시스템으로 이식을 위해 되도록 send를 사용하는 것이 좋다.






※ 윈도우 기반에서는...

◎ Visual C++에서 빈 소스 파일 하나를 연다.
Project -> Setting -> LINK 메뉴 -> Object/library modules: 의 끝부분에 ws2_32.lib 를 추가한다.


◎ #include <sys/socket.h>		->	#include <winsock2.h>

◎ UNIX 체계에서 사용하던 함수들의 헤더파일이 Windows 기반에서는 존재하지 않을 수도 있다.

◎ struct sockaddr_in		->	SOCKADDR_IN

◎ int sockfd;			->	SOCKET sockfd;
// 소켓 디스크립터			// 소켓 핸들


◎ main() 함수 내부에

WSADATA wsaDATA;		// 추가. WSADATA형의 변수를 선언한다.

if( WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
	fprintf(stderr, "WSAStartup Error"), exit(1);
				// 추가. WSAStartup() 은 socket의 버젼을 ws2_32 라이브러리에 전달한다.
// 프로그램이 끝날 때, 항상 WSACleanup()으로 리소스를 해제해야한다.
예제)

#include <winsock2.h>

main(){
WSADATA wsaDATA;

SOCKET sockfd;
// UNIX 기반의 int sockfd;
SOCKADDR_IN ina;
// UNIX 기반의 struct sockaddr_in ina;

if( WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
	fprintf(stderr, "WSAStartup Error"), exit(1);

... 내용

WSACleanup();

}




server 예제

#include <stdio.h>
#include <winsock2.h>

#define PORT 9999		// 서버의 9999번 포트를 연다
#define BACKLOG 5

void error(char *buf)
{
	puts(buf), exit(1);
}

main(){
WSADATA wsaData;

SOCKET server_sock;		// 서버의 socket을 생성
SOCKET client_sock;		// 클라이언트의 socket을 생성

SOCKADDR_IN server_addr;	// 네트워크의 정보를 담을 structure 생성.
SOCKADDR_IN client_addr;

int sizeof_sockaddr_in;
char msg[] = "Hello, World!";

	if( WSAStartup(MAKEWORD(2,2), &wsaData) == -1 )
		error("WSAStartup Error");


// socket 설정
// 프로그래머는 이것을 통해 네트워크와 대화를 한다.

	server_sock = socket(AF_INET, SOCK_STREAM, 0);
	if( server_sock == -1 )
		error("server socket error");


// 네트웍 정보 설정
// 이것은 프로그램이 socket과 연결할 정보를 담고있다.

	memset((SOCKADDR_IN *)&server_addr, 0, sizeof(SOCKADDR_IN));
	// struct sockaddr_in	->	SOCKADDR_IN
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = INADDR_ANY;	// 자신의 주소로 설정한다.
	server_addr.sin_port = htons(PORT);


// socket과 네트웍 정보를 bind()로 연결한다.

	if( bind(server_sock, (sockaddr *)&server_addr, sizeof(SOCKADDR_IN)) == -1 )
		error("bind() 에러");


// 준비는 끝났다!
// listen()으로 client의 연결 요청을 기다리자.
// client의 요청이 올 때 까지 서버는 여기서 기다린다.

	if( listen(server_sock, BACKLOG) == -1 )
		error("listen() 에러");


// client의 요청이 오면 server는 accept() 함수로 요청을 받아들인다.
// client와의 데이터 전송을 위해 client 소켓 디스크립터가 필요하다.

	sizeof_sockaddr_in = sizeof(SOCKADDR_IN);

	while(1)
	{
		client_sock = accept(server_sock, (sockaddr *)&client_addr, &sizeof_sockaddr_in);
		if( client_sock == -1 )
		{
			// accept가 실패하면 while의 처음으로 돌아가 다시 client를 기다린다.
			fprintf(stderr, "accept() 에러");
			continue;
		}

		send(client_sock, msg, sizeof(msg), 0);

		//close(client_sock);
		WSACleanup();
	}


	exit(0);
	// exit로 종료를 하면 모든 파일 디스크립터를 자동으로 닫고 종료한다.
}


※ 컴퓨터는 하나의 처리밖에 하지 못한다. 따라서 위의 소스는 하나의 client밖에 받아 들일 수 밖에 없다.
   어떻게 하면 여러 client를 동시에 받아들일 수 있을까?


실습

위의 server 에 접속 하는 client 프로그램을 짜고, Server가 보내는 메세지인 "Hello, World!"란 문장을 clinet 화면에 출력하도록 한다.


참고 사이트

An Introduction to Socket Programming : [1]

Beej's Guide to Network Programming : [2]

Beej's 번역판 : 서치엔진에서 찾아보세요. (무책임)

Socket Programming in Python : [3]

IPv6 Socket Programming : [4]


프로그래밍분류