'System Program/socket'에 해당되는 글 9건

  1. 2014.03.06 연결형 서버/클라이언트 예제 (클라이언트 포함)
  2. 2014.03.06 연결형 에코 서버 예제
  3. 2014.03.05 소켓옵션
  4. 2014.03.04 IP주소 및 포트 정보관련 시스템 함수
  5. 2014.03.04 IP주소 및 포트 관리
  6. 2014.03.04 주소 구조체
  7. 2014.03.04 호스트,네트워크간 변환 함수 정리
  8. 2014.03.03 기본 소켓시스템 함수 (비연결형)
  9. 2014.03.03 기본 소켓 시스템 함수 (연결형)

연결형 서버/클라이언트 예제 (클라이언트 포함)

System Program/socket 2014. 3. 6. 15:37

1. 서버 프로그램

 

클라이언트로부터 송신한 문자열을 stdout에 단지 출력한다.

 

서버에서는 accept()함수로부터 단지 하나의 새로운 소켓 디스크립터를 할당 받고

이 파일 디스크립터를 이용하여 클라이언트로부터 문자열을 받는다.

즉, 멀티프로세스, 멀티쓰레드 구현이 아니다. 이를 구현하기 위해서는

accept()함수로 계속적으로 새로운 소켓 파일 디스크립터를 생성받고 새로운 프로세스나 새로운 쓰레드를

통해서 다른 추가적인 작업 후 종료하는 방식으로 작성되야 할 것이다.

 

/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define READ_MAX_LEN 128

int main(int argc, char *argv[])
{
	int sockfd, acceptfd;
	ssize_t nread;
	struct sockaddr_in server_addr, client_addr;
	socklen_t server_addrlen, client_addrlen = 0;
	char buffer[READ_MAX_LEN] = {0};
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr("192.168.0.2");
	//server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(5000);
	
	server_addrlen = sizeof(server_addr);
	
	sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(sockfd < 0)
	{
		perror("socket : ");
		exit(EXIT_FAILURE);
	}
	
	if(bind(sockfd, (struct sockaddr *)&(server_addr), server_addrlen) < 0)
	{
		perror("bind : ");
		exit(EXIT_FAILURE);	
	}
	
	if(listen(sockfd, 5) < 0)
	{
		perror("listen : ");
		exit(EXIT_FAILURE);	
	}
	
	acceptfd = accept(sockfd, (struct sockaddr *)&(client_addr), &client_addrlen);
	if(acceptfd < 0)
	{
		perror("listen : ");
		exit(EXIT_FAILURE);	
	}	

	while(1)
	{
		memset(buffer, 0, READ_MAX_LEN);
		nread = read(acceptfd, buffer, READ_MAX_LEN);
		if(nread == -1)
		{
			perror("read : ");
			exit(EXIT_FAILURE);	
		}
			
		write(STDOUT_FILENO, buffer, strlen(buffer));
		
		if(strncmp(buffer, "bye", 3) == 0)
		{
			close(acceptfd);
			break;		
		}
	}
	
	close(sockfd);
	printf("exit process \n");
	exit(EXIT_SUCCESS);
}


 

2. 클라이언트 프로그램

 

단지 문자열을 서버단으로 전송하는 작업만을 수행한다.

 

/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define WRITE_MAX_LEN 128

int main(int argc, char *argv[])
{
	int sockfd;
	ssize_t nread = 0;
	char buffer[WRITE_MAX_LEN] = {0};	
	struct sockaddr_in ServerAddr;	
	
	ServerAddr.sin_family = AF_INET;
	ServerAddr.sin_addr.s_addr = inet_addr("192.168.0.2");
	ServerAddr.sin_port = htons(5000);
	
	sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(sockfd < 0)
	{
		perror("socket : ");
		exit(EXIT_FAILURE);
	}
	
	if(connect(sockfd, (struct sockaddr *)&(ServerAddr), sizeof(ServerAddr)) == -1)
	{
		perror("connect : ");
		exit(EXIT_FAILURE);
	}	
	
	while(1)
	{	
		memset(buffer, 0x00, WRITE_MAX_LEN);
		puts("input message : ");
		
		nread = read(STDIN_FILENO, buffer, WRITE_MAX_LEN /* SSIZE_MAX -1 */);
		if(nread == -1)
		{
			perror("read : ");
			exit(EXIT_FAILURE);
		}		

		if(write(sockfd, buffer, (size_t)nread) < 0)
		{
			perror("write : ");
			exit(EXIT_FAILURE);
		}

		if(strncmp(buffer, "bye", 3) == 0)
			break;		
	}
	
	close(sockfd);
	printf("exit process \n");
	exit(EXIT_SUCCESS);
}


 

'System Program > socket' 카테고리의 다른 글

연결형 에코 서버 예제  (0) 2014.03.06
소켓옵션  (0) 2014.03.05
IP주소 및 포트 정보관련 시스템 함수  (0) 2014.03.04
IP주소 및 포트 관리  (0) 2014.03.04
주소 구조체  (0) 2014.03.04
:

연결형 에코 서버 예제

System Program/socket 2014. 3. 6. 11:46

1. 서버 프로그램

 

단순히 클라이언트가 연결을 시도하여 연결이 성사되면  "hellow world from echo server program"

문자를 클라이언트에게 에코한다.  

 

/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

int main(int argc, char *argv[])
{
	struct sockaddr_in server_addr, client_addr;
	int sockfd, acceptfd;
	socklen_t server_addrlen, client_addrlen = 0;
	ssize_t n;
	char ClientAddrStr[INET_ADDRSTRLEN];
	char buffer[128] = "hellow world from echo server program \n";
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = inet_addr("192.168.0.2");
	//server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(5000);
	
	server_addrlen = sizeof(server_addr);
	
	sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(sockfd < 0)
	{
		perror("socket : ");
		exit(EXIT_FAILURE);
	}
	
	if(bind(sockfd, (struct sockaddr *)&(server_addr), server_addrlen) < 0)
	{
		perror("bind : ");
		exit(EXIT_FAILURE);	
	}
	
	if(listen(sockfd, 5) < 0)
	{
		perror("listen : ");
		exit(EXIT_FAILURE);	
	}
	
	while(1)
	{
		acceptfd = accept(sockfd, (struct sockaddr *)&(client_addr), &client_addrlen);
		if(acceptfd < 0)
		{
			perror("listen : ");
			exit(EXIT_FAILURE);	
		}
		else
		{
			// echo fucntion
			write(acceptfd, buffer, strlen(buffer));
			printf("client address = %s \n", inet_ntop(client_addr.sin_family, &(client_addr.sin_addr),
			ClientAddrStr, INET_ADDRSTRLEN));
			close(acceptfd);
		}
	}
	
	close(sockfd);
	
	exit(EXIT_SUCCESS);
}


 

 

2. 클라이언트 프로그램

 

telnet 192.168.0.2 5000 으로 동작을 확인할 수 있다.

 

더 고찰 사항은 서버가 연결해제된 상태에서 연결 시도를 수행할 때 발생하는 시그널 관련하여

클라이언트처리를 확인하기 위해서는 별도의 클라이언트 프로그램 구현이 필요할 것으로 보인다.

 

 

'System Program > socket' 카테고리의 다른 글

연결형 서버/클라이언트 예제 (클라이언트 포함)  (0) 2014.03.06
소켓옵션  (0) 2014.03.05
IP주소 및 포트 정보관련 시스템 함수  (0) 2014.03.04
IP주소 및 포트 관리  (0) 2014.03.04
주소 구조체  (0) 2014.03.04
:

소켓옵션

System Program/socket 2014. 3. 5. 10:23

1. 소개

 

소켓을 이용해서 프로그램을 개발할 때, 일반적인 소켓 API를 구성하여 작성하는 기본적인 구현 이외에

소켓 설정에 대한 수정을 통해서 더 정교한 제어를 할 수 있다.

소켓 설정에 대한 수정은 소켓 옵션 설정 시스템 함수를 통하여 이루어진다.

 

2. 소켓 옵션 함수

 

#include <sys/socket.h>

 

int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

 

성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴 

 

위의 함수에서 다른 매개변수은 쉽게 이해할 수 있다.

두번째 매개변수인 level에 대해서 정리하면 아래와 같다.

 

 level

설명 

참고문서 

 SOL_SOCKET

 socket() 함수 레벨에서 옵션 수정

 socket (7) manual 참조 

 IPPROTO_TCP

 TCP 소켓에 대한 옵션 수정 

 tcp (7) manual 참조 

 IPPROTO_UDP

 UDP 소켓에 대한 옵션 수정 

 udp (7) manual 참조 

 IPPROTO_IP

 IP 단계에 대한 옵션 수정  

 ip (7) manual 참조 

 

세번째 인자에 대한 가능한 optname은 참고문서에서 기술한 메뉴얼을 참조

 

3. 예시

 

완전한 예시라기 보다는 옵션 함수 사용법 정도 본다는 수준이다.

 

/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>

int main(int argc, char *argv[])
{
	static int reuseflag = 1;
	size_t sockbuffer = (188 * 7) * 150;
	socklen_t sockbufferlen = 0;
	int sockfd;
	struct sockaddr_in server_addr;

	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if(sockfd < 0)
	{
		perror("socket : ");
		exit(EXIT_FAILURE);
	}
	
	/* 연결 중에 잠시 발생한 에러로 연결해제된 주소에 대해 기존 kernel이 유지 중인 주소를 재 사용 */
	if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuseflag, (socklen_t)sizeof(reuseflag)) != 0)
	{
		perror("setsockopt : ");
		exit(EXIT_FAILURE);		
	}	

	/* socket buffer 수정 */
	if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sockbuffer, sizeof(sockbuffer)) != 0)
	{
		perror("setsockopt : ");
		exit(EXIT_FAILURE);		
	}	

	if(getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sockbuffer, &sockbufferlen) != 0)
	{
		perror("setsockopt : ");
		exit(EXIT_FAILURE);		
	}

	printf("socket buffer size = %d\n", sockbuffer); 
	
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(5000);
	
	if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
	{
		perror("bind : ");
		exit(EXIT_FAILURE);			
	}
	
	return 0;
}


 

 

 

 

 

 

:

IP주소 및 포트 정보관련 시스템 함수

System Program/socket 2014. 3. 4. 14:30

1. getaddrinfo() 함수

 

호스트와 서비스명에 해당하는 IP 주소와 포트로 찾아 변환한다.

기존 gethostbyname()과 getserverbyname() 함수를 대체하였다.

  

#include <sys/socket.h>

#include <netdb.h>

int getaddrinfo(const char *host, const char *service,

const struct addrinfo *hints, struct addrinfo **result);

성공하면 '0'을 리턴하고, 에러가 발생하면 '0'이 아닌 값을 리턴 

 

getaddrinfo()함수는 host, service, hist 값을 입력으로 result로부터 정보를 추출하는 구조이다.

addrinfo 구조체는 아래와 같다.

 

struct addrinfo{

int         ai_flags;                                     /* 입력 플래그 (AI_* 상수) */

int         ai_family;                                    /* 주소 패밀리 : AF_INET, AF_INET6 */ 

int        ai_socktype;                                /* 종류 : SOCK_STREAM, SOCK_DGRAM */

int        ai_protocol;                                 /* 소켓 프로토콜 */

size_t   ai_addrlen;                                 /* ai_addr 이 가르키는 구조체 크기 */

char *  ai_canonname;                            /* 공식 호스트 명 */

struct    sockaddr *ai_addr;                     /* 소켓 주소 구조체를 가르키는 포인터 */

struct    addrinfo *ai_next;                       /* 링크드 리스트에서 다음 구조체 */

}; 

 

1.1. hints 인자에 대한 고찰

 

hints 인자를 통해서 getaddrinfo() 함수에서 리턴하는 주소 구조체의 정보를 선택적으로 취할 수 있다.

ai_flags, ai_family, ai_socktype, ai_protocol만 설정할 수 있다.

 

ai_family에는 AF_INET, AF_INET6, AF_UNSPEC 설정 가능. 마지막 값은 모든 종류를 리턴하도록 강제한다.

ai_socktype에는 SOCK_DGRAM, SOCK_STREAM, 0이 가능, 0이면 모든 값을 리턴

ai_protocol에는 대충 '0'을 설정한다.

ai_flags는 getaddrinfo()함수의 수행 방식을 변경하는 비트 마스크다. '0'혹은 아래 값과 OR연산한다.

 

 flags

설명 

 AI_ADDRCONFIG

 로컬 시스템에 IPv4, IPv6 중에 하나가 있으면 리턴

 AI_ALL

 AI_V4MAPPED 참조

 AI_CANNAME

 host가 NULL이 아닌 경우, NULL로 종료하는 공식 호스트명 포인트 리턴

 AI_NUMERICHOST

 host를 숫자로 이뤄진 주소 문자열로 해석하도록 강제

 AI_NUMERICSERC

 service를 숫자 포트 번호로 해석하도록 강제

 AI_PASSIVE

 수동 접속에 적합한 소켓 구조체 리턴

 AI_V4MAPPED

 IPv6 주소를 발견하지 못하면 IPv4로 매핑된 주소를 리턴

 

2. addrinfo 리스트 해제 : freeaddrinfo()

 

getaddrinfo() 함수는 result가 가르키는 모든 구조체 메모리를 동적으로 할당한다.

getaddrinfo() 함수를 호출한 곳에서 해당 구조체를 사용하고 메모리 해제를 책임져야 한다.

 

#include <sys/socket.h>

#include <netdb.h>

void freeaddrinfo(struct addrinfo *result);

 

3. 에러 진단 : gai_strerror()

 

에러가 발생한 경우 아래 값 중에 하나를 에러코드로 리턴한다.

 

#include <netdb.h>

const char *gai_strerror(int errcode);

아래 에러 상수에 해당하는 문자열을 리턴 

 

 에러 상수

설명 

 EAI_ADDRFAMILY

 hints.ai_family에 해당하는 호스트 주소가 없다

 EAI_AGAIN

 이름 해석 일시 오류(나중에 다시 시도)

 EAI_BADFLAGS

 hints.ai_flags에 잘못된 플레그 설정

 EAI_FAIL

 이름 서버에 접속하면서 회복할 수 없는 실패가 생성

 EAI_FAMILY

 hints.family에 설정한 주소 패밀리를 지원할 수 없음.

 EAI_MEMORY

 메모리 할당 에러

 EAI_NODATA

 host에 해당하는 주소를 찾을 수 없음

 EAI_NONAME

 host나 service를 알수 없음.

 EAI_OVERFLOW

 인자 버퍼 오버플로우

 EAI_SERVICE

 hints.ai_socktype에 알맞은 service를 지원하지 않음.

 EAI_SOCKTYPE

 hints.ai_socktype을 지원하지 않음.

 EAI_SYSTEM

 errno로 시스템 에러를 리턴

 

4. getnameinfo() 함수

 

getaddrinfo() 함수와 정반대의 동작을 수행한다.

 

#include <sys/socket.h>

#include <netdb.h>

int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,

char *host, size_t hostlen, char *service, size_t servlen, int flags);

성공하면 '0'을 리턴, 에러가 발생하면 '0'이 아닌 값을 리턴 

 

sockaddr 구조체를 통해 address 정보를 주면 host와 service에 대한 정보를 얻을 수 있다.

flags에 대한 자세한 내용은 아래 표를 참조

 

flags 

설명 

 NI_DGRAM

 데이터그램 소켓에 대한 정보를 리턴하도록 강제한다.

 NI_NAMEREQD

 호스트명을 찾을 수 없으면 숫자를 기본적으로 리턴하나 해당 플레그로 에러 리턴

 NI_NOFQDN

 기본적으로 FQDN을 리턴, 호스트가 로컬일 경우만 FQDN의 첫 부분만 리턴

 NI_NUMERICHOST

 DNS 대신 host에 숫자 형식의 문자열을 리턴하도록 강제

 NI_NUMERICSERV

 십진수 포트 번호를 리턴하도록 강제, /etc/services에 검색하지 않음.

 

5. 구형 유사 함수

 

5.1. IP 주소를  얻어오는 함수

 

#include <netdb.h>

extern int h_errno;

struct hostent *gethostbyname(const char *name);

struct hostent *gethostbyaddr(const char *addr, socklen_t len, int type);

성공하면 hostent 구조체의 포인터를 리턴, 에러가 발생하면 NULL을 리턴 

 

5.2. 포트 정보를 얻어오는 함수

 

/etc/services 파일에서 레코드를 가져오는 함수들이다.  

 

#include <netdb.h>

struct servent *getservbyname(const char *name, const char *proto);

struct servent *getservbyport(int port, const char *proto);

성공하면 servent구조체의 포인터를 리턴, 에러가 발생하면 NULL을 리턴 

 

 

 

 

'System Program > socket' 카테고리의 다른 글

연결형 에코 서버 예제  (0) 2014.03.06
소켓옵션  (0) 2014.03.05
IP주소 및 포트 관리  (0) 2014.03.04
주소 구조체  (0) 2014.03.04
호스트,네트워크간 변환 함수 정리  (0) 2014.03.04
:

IP주소 및 포트 관리

System Program/socket 2014. 3. 4. 14:06

1. DNS 기본 지식

 

네트워크 상에 다른 호스트에 접속하기 위해서 IP 주소보다는 사람이 읽을 수 있는 이름으로 찾는 것이

더 효율적일 때가 많다.

DNS가 나오기 전에는 호스트 자체적으로 /etc/hosts 파일에 호스트와 IP정보를 매핑하여 관리를 하였다.

하지만 네트워크 규모의 증가로 이를 커버하고자 DNS 기술이 등장하였다.

 

2. 호스트 상에서 IP 주소 획득 순서

  

1) /etc/hosts 검색

   

    위의 파일에 찾고자하는 호스트가 목록에 존재하면 그 주소를 이용하고 그렇지 않은 경우 아래 2)단계 수행  

 

2) /etc/resolv.conf

  

    DNS 서버 주소를 확인하고 DNS서버에 호스트에 대한 주소를 얻어온다.  

 

 

만약 /etc/resolv.conf 파일에 DNS서버 설정에 문제가 없더라도 /etc/hosts 파일에 정상적으로 찾으면

호스트 주소를 얻어오는데는 문제가 없다.

 

3. 호스트 포트 번호 관리 

 

포트 번호도 잘 알려진 번호에 대해서는 IANA에서 중앙 집중적으로 등록하여 관리한다.

이들 각 포트 번호에는 대응하는 서비스명이 있다.

포트 번호는 IP주소 비해 변경이 없는 편이여서 DNS처럼 관리는 하지 않으나 호스트 내부적으로

/etc/services 파일을 통해서 관리를 한다.

 

# Network services, Internet style
#
# Note that it is presently the policy of IANA to assign a single well-known
# port number for both TCP and UDP; hence, officially ports have two entries
# even if the protocol doesn't support UDP operations.
#
# Updated from http://www.iana.org/assignments/port-numbers and other
# sources like http://www.freebsd.org/cgi/cvsweb.cgi/src/etc/services .
# New ports will be added on request if they have been officially assigned
# by IANA and used in the real-world or are needed by a debian package.
# If you need a huge list of used numbers please install the nmap package.

tcpmux  1/tcp    # TCP port service multiplexer
echo  7/tcp
echo  7/udp
discard  9/tcp  sink null
discard  9/udp  sink null
systat  11/tcp  users
daytime  13/tcp
daytime  13/udp
netstat  15/tcp
qotd  17/tcp  quote
msp  18/tcp    # message send protocol
msp  18/udp
chargen  19/tcp  ttytst source
chargen  19/udp  ttytst source
ftp-data 20/tcp
ftp  21/tcp
fsp  21/udp  fspd
ssh  22/tcp    # SSH Remote Login Protocol
ssh  22/udp
telnet  23/tcp
smtp  25/tcp  mail
time  37/tcp  timserver
time  37/udp  timserver
rlp  39/udp  resource # resource location
nameserver 42/tcp  name  # IEN 116
whois  43/tcp  nicname
tacacs  49/tcp    # Login Host Protocol (TACACS)

 

 

 

'System Program > socket' 카테고리의 다른 글

소켓옵션  (0) 2014.03.05
IP주소 및 포트 정보관련 시스템 함수  (0) 2014.03.04
주소 구조체  (0) 2014.03.04
호스트,네트워크간 변환 함수 정리  (0) 2014.03.04
기본 소켓시스템 함수 (비연결형)  (0) 2014.03.03
:

주소 구조체

System Program/socket 2014. 3. 4. 11:10

1. 유닉스 주소 체계 (내부 내트워크)

  

struct sockaddr_un{

sa_family_t sun_family;                    /* 항상 AF_UNIX */

char sun_path[108];                       /* NULL로 종료되는 소켓 경로명 */

}; 

 

2. IPv4 소켓 주소

 

struct in_addr {

in_addr_t s_addr;                            /* IPv4 4바이트 부호없는 32비트 정수 */ 

};

 

struct sockaddr_in {

sa_family_t sin_family;                     /* 주소 패밀리 AF_INET 설정 */

in_port_t    sin_port;                        /* 포트 번호 */

struct in_addr sin_addr;                  /* IPv4 주소 */

unsigned char __pad[X];                /* 패딩 */

}; 

 

3. IPv6 소켓 주소

 

struct in6_addr {

in_addr_t s6_addr[16];                            /* IPv6 16바이트 부호없는 128비트 정수 */ 

};

 

struct sockaddr_in6 {

sa_family_t sin6_family;                     /* 주소 패밀리 AF_INET6 설정 */

in_port_t    sin6_port;                        /* 포트 번호 */

uint32_t sin6_flowinfo;                       /* 흐름 정보 */

struct in6_addr sin6_addr;                  /* IPv6 주소 */

uint32_t sin6_scope_id;                     /* 범위 ID */ 

}; 

 

 

위의 주소 체계는 결국 소켓함수 API에서 사용될 때 struct sockaddr로 변환되어야 한다.

아래 소켓 시스템 함수 domain 표에서 5번째 필드 참조  

 

http://chipmaker.tistory.com/entry/기본-소켓-시스템-함수

 

 

:

호스트,네트워크간 변환 함수 정리

System Program/socket 2014. 3. 4. 10:42

호스트는 네트워크 상에 연결된 단말기를 말한다.

호스트에서 작업한 내용을 네트워크로 소켓 API로 전송 시 이종 시스템간 및 네트워크 특성을

반영하여 변환이 필요하다. 이 때 사용되는 함수들을 정리한다.

 

1. 호스트와 네트워크 바이트 순서 변경 함수

 

호스트는 빅엔디안/리틀엔디안 시스템에 따라 둘 중 하나를 사용할 수 있다.

하지만 네트워크 전송 시에는 네트워크 바이트 순서로 바꾸어 주어야 한다.

네트워크는 빅엔디안을 사용한다. 이들 바이트 순서간의 상호 변환하는 함수를 기술한다.

 

#include <arp/inet.h>

uint16_t htons(uint16_t host_uint16);

host_uint16을 네트워크 바이트 순서로 변환한 값을 리턴

uint32_t htonl(uint32_t host_uint32);

host_uint32을 네트워크 바이트 순서로 변환한 값을 리턴

uint16_t ntohs(uint16_t net_uint16);

net_uint16을 호스트 바이트 순서로 변환한 값을 리턴

uint32_t ntohl(uint32_t net_uint32);

net_uint32을 호스트 바이트 순서로 변환한 값을 리턴

 

네트워크 프로그램 시에는 바이트 순서가 호스트와 네크워크간에 일치하더라도 항상 위의 함수로 변환하는

습관을 가져야 이식성을 높일 수 있다.

 

2. 호스트와 서비스 변환 함수

 

컴퓨터 내부에서 IP 주소와 포트 번호는 이진 형식으로 표현한다.

사람이 친숙한 포멧과 기계가 알아먹을 수 있는 포멧간의 상호 변환 함수이다.

일반적인 사람이 사용하는 IP 포멧은 127.0.0.1과 같은 형식이다.

 

#include <arp/inet.h>

int inet_pton(int domain, const char *src_str, void *addrptr);

src_str이 프리젠테이션 포멧이 아닌 경우에는 '0'을, 에러가 발생하면 '-1'을 리턴, 변환 성공하면 '1'을 리턴

const char *inet_ntop(int domain, const void *addrptr, char *dst_str, size_t len);

성공하면 dst_str의 포인터를 리턴, 에러가 발생하면 NULL을 리턴 

 

[예제]

/*
* main.c -- C Test App.
*
* Copyright (C) 2012-2013, 2013 heesoon.kim <chipmaker.tistory.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
	struct sockaddr_in sa;
	char buffer[INET_ADDRSTRLEN] = {0};
	
	if(argc < 2)
	{
		printf("not enough parameter [usage]: %s 'ip address' ", argv[0]);
		exit(EXIT_FAILURE);
	}	
	
	inet_pton(AF_INET, argv[1], &(sa.sin_addr));
	
	if(inet_ntop(AF_INET, &(sa.sin_addr), buffer, INET_ADDRSTRLEN) == NULL)
	{
		perror("inet");
		exit(EXIT_FAILURE);
	}
	
	printf("%s\n", buffer);
	
	exit(EXIT_SUCCESS);
}


 

위의 함수는 IPv6까지 지원하는 함수이다. 과거에는 아래의 함수를 많이 사용하였으나 IPv4만을 지원하는

한계로 사용하지 않는 것이 좋다.

 

#include <arp/inet.h>

int inet_aton(const char *str, struct in_addr *addr);

str이 유효한 점으로 구분하는 십진수 주소이면 '1'을 리턴, 에러가 발생하면 '0'을 리턴

char *inet_ntoa(struct in_addr addr);

addr을 점으로 구분하는 십진수 문자열로 변환한 결과를 가르키는 포인터값을 리턴한다.   

 

 

 

 

 

:

기본 소켓시스템 함수 (비연결형)

System Program/socket 2014. 3. 3. 11:48

1. 소켓 생성

 

소켓 생성은 연결형 소켓 생성함수와 동일하게 socket()함수를 사용한다.

단, socket()함수 사용 시 type에 SOCKET_DGRAM을 준다.

 

2.  소켓 결속

 

다른 응용 프로그램에서 보낸 데이터를 전송 받으려면 bind()를 이용해 자신의 소켓을 이미 알려진

주소로 결속한다.

 

여기까지는 연결형 소켓 생성 과정과 동일하다. 자세한 함수 정의는 연결형 소개 부분 참조

 

3. 데이터 교환

 

3.1. 데이터 전송

 

#include <sys/socket.h>

ssize_t sendto(int sockfd, void *buffer, size_t length, int flags,

const struct sockaddr *dest_addr, socklen_t addrlen);

전송한 바이트 수를 리턴한다. 에러가 발생하면 '-1'을 리턴  

 

첫번째부터 세번째까지는 write()와 동일하고, flags는 send() 함수를 참조

뒤의 주소는 데이터를 보낼 곳의 주소를 지정한다.

 

3.2. 데이터 수신

 

#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags,

struct sockaddr *src_addr, socklen_t *addrlen);

전송된 바이트 수를 리턴한다. EOF인 경우에는 '0'을 에러가 발생하면 '-1'을 리턴한다.  

 

세번째까지 인자는 위와 동일하고 주소는 데이터를 전송한 원격지의 주소 정보를 갖는다.

 

4. 연결형 함수의 변칙적 사용

 

연결형 소켓에서 사용하는 read()/write()함수를 비연결형에서도 사용할 수 있다.

다만 비연결형에서 사용하기 위해서 설정을 해 주어야 한다. 일반적이지 아니니 이런 것이 있다고만 생각하자.

 

비연결형 소켓에 또한 연결형 소켓에서 사용하는 connect() 함수를 사용할 수 있다.

이런 경우를 연결형 데이터그램 소켓이라고 한다.

이 연결형 데이터 그램 소켓일 경우에는 read()/write()함수를 사용할 수 있고,

연결형 소켓처럼 지정한 주소에서만 데이터 송수신이 가능하게 할 수 있는 장점이 있다.

 

5. 흐름도

 

 

 

 

 

:

기본 소켓 시스템 함수 (연결형)

System Program/socket 2014. 3. 3. 09:18

1. socket() 함수

 

#include <sys/socket.h>

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

성공하면 파일 디스크립터을 리턴, 에러가  발생하면 '-1'을 리턴

 

1.1. domain 매개 변수

 

도메인 

통신수단 

응용프로그램 간의 통신 

주소포멧 

주소구조체 

 AF_UNIX

 커널 내부 

 동일 호스트

 경로명

 sockaddr_un

 AF_INET

 IPv4 이용

 IPv4로 연결된 호스트

 32비트 주소 +

 16비트 포트 번호

 sockaddr_in

 AF_INET6

 IPv6 이용

 IPv6로 연결된 호스트

 128비트 주소

 16비트 포트 번호

 sockaddr_in6

 

1.2. type 매개 변수

 

 종류

설명 

 SOCK_STREAM

 연결지향적 양방향 신뢰성 있는 통신 방식

 SOCK_RAW

 socket API에 의한 데이터 가공없이 데이터를 받고자 할 때 사용

 SOCK_DGRAM

 SOCKET_STREAM과 상반되는 개념

 

1.3. protocol 매개 변수

 

IPPROTO_TCP : TCP 프로토콜로 AF_INET 도메인과 SOCK_STREAM 유형 사용 시.

IPPROTO_UDP : UDP 프로토콜로 AF_INET 도메인과 SOCK_DGRAM 유형 사용 시.

IPPROTO_RAW : SOCK_RAW 사용하는 경우 시

 

일반적으로 '0'을 설정, 자세한 설명은 man page를 참조하는 것이 좋다.

 

2. bind() 시스템 함수

 

소켓을 주소에 결속한다.  아래 sockaddr 구조에는 결속된 주소 정보를 담고 있다.

전화기에 비유하자면 전화번호를 전화국 (커널)에 등록하는 절차에 해당한다.

 

#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

성공하면 '0'을 리턴, 에러가  발생하면 '-1'을 리턴

 

domain 매개 변수 표, 5번째 열에 보여지는 것처럼 도메인에 따라 주소체계가 모두 다르다.

모두 다른 주소체계를 위의 sockaddr 구조체를 통해서 통일화할 수 있다.

sockaddr 구조체는 아래와 같다.

 

struct sockaddr {

sa_family_t     sa_family;                /* 주소 패밀리 (AF_* 상수) */

char              sa_data[14];            /* 소켓 주소 (소켓도메인에 따라 크기가 변한다) */

};

 

3. listen() 시스템 함수

 

커널에게 bind에서 결속한 주소로 연결을 받기위해 대기 중임을 알린다.

전화기로 따지면 전화를 받을 준비가 모두된 상태를 의미한다.

이처럼 연결이 되기를 기다리는 형태의 동작을 수동형 연결이라 하며, 일반적으로 서버쪽의 동작이다.   

 

 #include <sys/socket.h>

int listen(int sockfd, int backlog); 

성공하면 '0'을 리턴, 에러가  발생하면 '-1'을 리턴

 

위에서 backlog 값은 연결이 지연된 요청에 대한 개수를 리턴한다.

리눅스에서 이 값은 128로 설정되어 있으며, /proc/sys/net/core/somaxconn 파일을 통해서 런타임으로

한도값을 설정할 수 있다.

 

4. accept() 시스템 함수

 

연결을 요청한 소켓을 수락하는 동작이다.

전화기로 따지면 수화기를 드는 동작에 해당한다.   

 

 #include <sys/socket.h>

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

성공하면 파일 디스크립터를 리턴하고, 에러가 발생하면 '-1'을 리턴

 

accept에서 핵심은 connect() 호출한 상대방의 소켓과 새로운 연결을 맺을 소켓을 생성하는 것이다.

따라서 socket()함수를 통해서 생성된 소켓은 연결을 계속 기다리는 소켓으로 남아있고,

accept()를 통해서 새로 생성된 소켓을 통해서 connect() 호출한 쪽과 통신을 할 수 있다.

따라서 accept()함수의 리턴값에 새로운 소켓을 리턴하는 것이다.

 

뒤이은 sockaddr 구조체 포인터를 통해서 connect()을 요청한 쪽의 주소를 얻을 수 있다.

상대방의 주소가 필요없을 경우, 각각 NULL, '0'을 설정하면 된다.  

 

5. connect() 시스템 함수

 

위에서 listen()으로 대기중인 소켓에 연결을 요청할 때 사용하는 시스템 함수이다.

능동형 연결에 해당하며, 일반적으로 클라이언트쪽의 동작이다.  

 

 #include <sys/socket.h>

int connect(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 

성공하면 '0'을 리턴하고, 에러가 발생하면 '-1'을 리턴

 

클라이언트쪽에서는 별도의 bind() 동작을 통해서 주소를 결속하는 작업은 없다.

connect()함수의 주소관련 매개변수를 통해서 bind() 동작과 유사한 동작은 포함되어 있다.

 

6. 소켓을 통한 데이터 전송 및 수신 함수 

 

연결 지향형 소켓 응용에서는 read()/write()함수를 통해서 데이터를 송수신한다.

여기서 중요한 점은 양단간에 연결이 끊어졌을 때, 가장 기본적인 에러처리에 대한 것이다.

연결이 끊어진 곳에 데이터를 읽을려고 시도하면 EOF를 리턴한다.

반대로 데이터를 쓰려고 할 경우, 해당 프로그램은 SIGPIPE 시그널을 받게되고,

시스템 호출은 EPIPE로 실패한다. 시그널 핸들러가 없는 경우 errno를 확인하는 것으로 에러처리하는 것이 바람직하다.

 

7. close() 시스템 함수

 

연결된 socket를 닫을 때 사용한다.

 

8. 연결형 서버-클라이언트 흐름도

 

 

 

 

 

 

 

 

 

 

: