유닉스 소켓-퀵 가이드
소켓을 사용하면 동일하거나 다른 시스템에서 서로 다른 두 프로세스 간의 통신이 가능합니다. 더 정확하게 말하면 표준 Unix 파일 설명자를 사용하여 다른 컴퓨터와 대화하는 방법입니다. Unix에서 모든 I / O 작업은 파일 설명자를 쓰거나 읽음으로써 수행됩니다. 파일 설명자는 열린 파일과 관련된 정수일 뿐이며 네트워크 연결, 텍스트 파일, 터미널 또는 다른 것이 될 수 있습니다.
프로그래머에게 소켓은 저수준 파일 설명자처럼 보이고 동작합니다. 이것은 read () 및 write ()와 같은 명령이 파일 및 파이프와 동일한 방식으로 소켓에서 작동하기 때문입니다.
소켓은 2.1BSD에서 처음 도입되었으며 이후 4.2BSD로 현재의 형태로 개선되었습니다. 소켓 기능은 이제 대부분의 최신 UNIX 시스템 릴리스에서 사용할 수 있습니다.
소켓은 어디에 사용됩니까?
Unix 소켓은 클라이언트-서버 애플리케이션 프레임 워크에서 사용됩니다. 서버는 클라이언트의 요청에 따라 일부 기능을 수행하는 프로세스입니다. FTP, SMTP 및 POP3와 같은 대부분의 애플리케이션 수준 프로토콜은 소켓을 사용하여 클라이언트와 서버 간의 연결을 설정 한 다음 데이터를 교환합니다.
소켓 유형
사용자가 사용할 수있는 소켓에는 네 가지 유형이 있습니다. 처음 두 개는 가장 일반적으로 사용되며 마지막 두 개는 거의 사용되지 않습니다.
프로세스는 동일한 유형의 소켓간에 만 통신하는 것으로 간주되지만 다른 유형의 소켓 간의 통신을 방지하는 제한은 없습니다.
Stream Sockets− 네트워크 환경에서의 배송이 보장됩니다. 스트림 소켓을 통해 "A, B, C"항목 3 개를 보내면 "A, B, C"라는 동일한 순서로 도착합니다. 이 소켓은 데이터 전송을 위해 TCP (Transmission Control Protocol)를 사용합니다. 배송이 불가능한 경우 발신자에게 오류 표시기가 표시됩니다. 데이터 레코드에는 경계가 없습니다.
Datagram Sockets− 네트워크 환경에서의 배송은 보장되지 않습니다. 스트림 소켓에서와 같이 열린 연결을 가질 필요가 없기 때문에 연결이 없습니다. 목적지 정보로 패킷을 만들어 전송합니다. UDP (User Datagram Protocol)를 사용합니다.
Raw Sockets− 소켓 추상화를 지원하는 기본 통신 프로토콜에 대한 사용자 액세스를 제공합니다. 이러한 소켓은 일반적으로 데이터 그램 지향적이지만 정확한 특성은 프로토콜에서 제공하는 인터페이스에 따라 다릅니다. 원시 소켓은 일반 사용자를위한 것이 아닙니다. 이들은 주로 새로운 통신 프로토콜을 개발하는 데 관심이 있거나 기존 프로토콜의 좀 더 복잡한 기능에 대한 액세스 권한을 얻기 위해 제공되었습니다.
Sequenced Packet Sockets− 레코드 경계가 유지된다는 점을 제외하면 스트림 소켓과 유사합니다. 이 인터페이스는 NS (Network Systems) 소켓 추상화의 일부로 만 제공되며 대부분의 심각한 NS 애플리케이션에서 매우 중요합니다. 시퀀스 패킷 소켓을 사용하면 사용자가 보낼 데이터와 함께 프로토 타입 헤더를 작성하거나 모든 나가는 데이터에 사용할 기본 헤더를 지정하고 사용자가 들어오는 패킷에서 헤더를받을 수 있도록합니다.
다음은 무엇입니까?
다음 몇 장은 socket을 사용하여 서버 및 클라이언트 프로그램을 작성하기 전에 기초를 강화하고 기초를 준비하기위한 것 입니다. 클라이언트 및 서버 프로그램을 작성하는 방법을 직접보고 싶다면 그렇게 할 수 있지만 권장하지 않습니다. 프로그래밍을 진행하기 전에 단계별로 이동하여 초기 몇 장을 완료하여 기반을 만드는 것이 좋습니다.
실제 작업을 진행하기 전에 네트워크 주소 (IP 주소)에 대해 조금 논의 해 보겠습니다.
IP 호스트 주소 또는보다 일반적으로 IP 주소는 인터넷에 연결된 호스트를 식별하는 데 사용됩니다. IP는 인터넷 프로토콜을 의미하며 인터넷 전체 네트워크 아키텍처의 인터넷 계층을 나타냅니다.
IP 주소는 4 개의 8 비트 숫자 또는 8 진수로 해석되는 32 비트 수량입니다. 각 IP 주소는 참여하는 사용자 네트워크, 네트워크의 호스트 및 사용자 네트워크의 클래스를 고유하게 식별합니다.
IP 주소는 일반적으로 N1.N2.N3.N4 형식의 점으로 구분 된 10 진수 표기법으로 작성됩니다. 여기서 각 Ni는 0에서 255 사이의 10 진수 (00에서 FF 16 진수)입니다.
주소 등급
IP 주소는 IANA ( Internet Assigned Numbers Authority) 에서 관리하고 생성합니다 . 다섯 가지 주소 클래스가 있습니다. IP 주소의 처음 4 비트를 검사하여 IP 주소가 속한 클래스를 확인할 수 있습니다.
Class A 주소는 0xxx, 또는 1 to 126 소수.
Class B 주소는 10xx, 또는 128 to 191 소수.
Class C 주소는 110x, 또는 192 to 223 소수.
Class D 주소는 1110, 또는 224 to 239 소수.
Class E 주소는 1111, 또는 240 to 254 소수.
로 시작하는 주소 01111111, 또는 127 10 진수, 루프백 및 로컬 시스템에서 내부 테스트 용으로 예약 됨 [이를 테스트 할 수 있습니다. 항상 ping 할 수 있어야합니다. 127.0.0.1, 자신을 가리키는]; 클래스 D 주소는 멀티 캐스팅 용으로 예약되어 있습니다. 클래스 E 주소는 향후 사용을 위해 예약되어 있습니다. 호스트 주소에 사용해서는 안됩니다.
예
Class | Leftmost bits | Start address | Finish address |
ㅏ | 0xxx | 0.0.0.0 | 127.255.255.255 |
비 | 10xx | 128.0.0.0 | 191.255.255.255 |
씨 | 110 배 | 192.0.0.0 | 223.255.255.255 |
디 | 1110 년 | 224.0.0.0 | 239.255.255.255 |
이자형 | 1111 년 | 240.0.0.0 | 255.255.255.255 |
서브넷
서브넷 또는 서브 네트워킹은 기본적으로 네트워크에서 분기하는 것을 의미합니다. 조직의 네트워크, 다른 물리적 미디어 (예 : 이더넷, FDDI, WAN 등) 사용, 주소 공간 보존 및 보안과 같은 다양한 이유로 수행 될 수 있습니다. 가장 일반적인 이유는 네트워크 트래픽을 제어하는 것입니다.
서브넷의 기본 아이디어는 IP 주소의 호스트 식별자 부분을 두 부분으로 분할하는 것입니다.
- 네트워크 주소 자체 내의 서브넷 주소. 과
- 서브넷의 호스트 주소.
예를 들어 일반적인 클래스 B 주소 형식은 N1.N2.SH입니다. 여기서 N1.N2는 클래스 B 네트워크를 식별하고 8 비트 S 필드는 서브넷을 식별하며 8 비트 H 필드는 서브넷의 호스트를 식별합니다.
숫자 측면에서 호스트 이름은 기억하기 어렵 기 때문에 Takshila 또는 Nalanda와 같은 일반 이름으로 불립니다. 주어진 이름에 해당하는 점으로 구분 된 IP 주소를 찾기 위해 소프트웨어 응용 프로그램을 작성합니다.
주어진 영숫자 호스트 이름을 기반으로 점으로 구분 된 IP 주소를 찾는 프로세스는 다음과 같습니다. hostname resolution.
호스트 이름 확인은 고용량 시스템에 상주하는 특수 소프트웨어에 의해 수행됩니다. 이러한 시스템을 DNS (Domain Name Systems)라고하며 IP 주소 및 해당 일반 이름의 매핑을 유지합니다.
/ etc / hosts 파일
호스트 이름과 IP 주소 간의 대응은 hosts 라는 파일에서 유지됩니다 . 대부분의 시스템에서이 파일은/etc 예배 규칙서.
이 파일의 항목은 다음과 같습니다.
# This represents a comments in /etc/hosts file.
127.0.0.1 localhost
192.217.44.207 nalanda metro
153.110.31.18 netserve
153.110.31.19 mainserver centeral
153.110.31.20 samsonite
64.202.167.10 ns3.secureserver.net
64.202.167.97 ns4.secureserver.net
66.249.89.104 www.google.com
68.178.157.132 services.amrood.com
두 개 이상의 이름이 주어진 IP 주소와 연관 될 수 있습니다. 이 파일은 IP 주소에서 호스트 이름으로 또는 그 반대로 변환하는 동안 사용됩니다.
이 파일을 편집 할 수있는 액세스 권한이 없으므로 IP 주소와 함께 호스트 이름을 입력하려면 루트 권한이 있어야합니다.
대부분의 넷 애플리케이션은 클라이언트-서버 아키텍처를 사용하는데, 이는 두 개의 프로세스 또는 일부 정보를 교환하기 위해 서로 통신하는 두 개의 애플리케이션을 의미합니다. 두 프로세스 중 하나는 클라이언트 프로세스로 작동하고 다른 프로세스는 서버로 작동합니다.
클라이언트 프로세스
이것은 일반적으로 정보를 요청하는 프로세스입니다. 응답을받은 후이 프로세스는 종료되거나 다른 처리를 수행 할 수 있습니다.
Example, 인터넷 브라우저는 하나의 HTML 웹 페이지를 얻기 위해 웹 서버에 요청을 보내는 클라이언트 애플리케이션으로 작동합니다.
서버 프로세스
이것은 클라이언트로부터 요청을받는 프로세스입니다. 클라이언트로부터 요청을받은 후이 프로세스는 필요한 처리를 수행하고 요청 된 정보를 수집하여 요청자 클라이언트로 보냅니다. 완료되면 다른 고객에게 서비스를 제공 할 준비가됩니다. 서버 프로세스는 항상 경고를 받고 들어오는 요청을 처리 할 준비가되어 있습니다.
Example − 웹 서버는 인터넷 브라우저의 요청을 계속 대기하고 브라우저에서 요청을받는 즉시 요청 된 HTML 페이지를 선택하여 해당 브라우저로 다시 보냅니다.
클라이언트는 서버의 주소를 알아야하지만 서버는 연결이 설정되기 전에 주소 나 클라이언트의 존재 여부를 알 필요가 없습니다. 연결이 설정되면 양측이 정보를주고받을 수 있습니다.
2 계층 및 3 계층 아키텍처
클라이언트-서버 아키텍처에는 두 가지 유형이 있습니다.
2-tier architecture−이 아키텍처에서 클라이언트는 서버와 직접 상호 작용합니다. 이러한 유형의 아키텍처에는 몇 가지 보안 허점과 성능 문제가있을 수 있습니다. Internet Explorer와 Web Server는 2 계층 아키텍처에서 작동합니다. 여기서 보안 문제는 SSL (Secure Socket Layer)을 사용하여 해결됩니다.
3-tier architectures−이 아키텍처에서는 클라이언트와 서버 사이에 하나 이상의 소프트웨어가 있습니다. 이 미들 소프트웨어를 '미들웨어'라고합니다. 미들웨어는 부하가 많은 경우 모든 보안 검사 및 부하 분산을 수행하는 데 사용됩니다. 미들웨어는 클라이언트로부터 모든 요청을 받고 필요한 인증을 수행 한 후 해당 요청을 서버로 전달합니다. 그런 다음 서버는 필요한 처리를 수행하고 응답을 미들웨어로 다시 보내고 마지막으로 미들웨어는이 응답을 클라이언트에 다시 전달합니다. 3 계층 아키텍처를 구현하려면 웹 서버와 웹 브라우저 사이에 Web Logic 또는 WebSphere 소프트웨어와 같은 미들웨어를 유지할 수 있습니다.
서버 유형
보유 할 수있는 두 가지 유형의 서버가 있습니다.
Iterative Server− 이것은 서버 프로세스가 하나의 클라이언트에 서비스를 제공하고 첫 번째 요청을 완료 한 후 다른 클라이언트의 요청을받는 가장 간단한 형태의 서버입니다. 한편 다른 클라이언트는 계속 기다리고 있습니다.
Concurrent Servers−이 유형의 서버는 한 프로세스가 더 오래 걸리고 다른 클라이언트가 오래 기다릴 수 없기 때문에 한 번에 많은 요청을 처리하기 위해 여러 동시 프로세스를 실행합니다. Unix에서 동시 서버를 작성하는 가장 간단한 방법 은 자식 프로세스를 포크 하여 각 클라이언트를 개별적으로 처리하는 것입니다.
클라이언트를 만드는 방법
연결을 설정하기위한 시스템 호출은 클라이언트와 서버에 대해 다소 다르지만 둘 다 소켓의 기본 구성을 포함합니다. 두 프로세스 모두 자체 소켓을 설정합니다.
클라이언트 측에서 소켓을 설정하는 단계는 다음과 같습니다.
다음으로 소켓 만들기 socket() 시스템 호출.
다음을 사용하여 소켓을 서버 주소에 연결합니다. connect() 시스템 호출.
데이터를 보내고받습니다. 이를 수행하는 방법에는 여러 가지가 있지만 가장 간단한 방법은read() 과 write() 시스템 호출.
서버를 만드는 방법
서버 측에서 소켓을 설정하는 단계는 다음과 같습니다.
다음으로 소켓 만들기 socket() 시스템 호출.
다음을 사용하여 소켓을 주소에 바인딩하십시오. bind()시스템 호출. 인터넷에있는 서버 소켓의 경우 주소는 호스트 시스템의 포트 번호로 구성됩니다.
와의 연결을 들어보십시오. listen() 시스템 호출.
다음과의 연결을 수락합니다. accept()시스템 호출. 이 호출은 일반적으로 클라이언트가 서버에 연결할 때까지 연결을 차단합니다.
다음을 사용하여 데이터 송수신 read() 과 write() 시스템 호출.
클라이언트 및 서버 상호 작용
다음은 완전한 클라이언트 및 서버 상호 작용을 보여주는 다이어그램입니다.
Unix 소켓 프로그래밍에서는 주소, 포트 및 기타 정보에 대한 정보를 보유하기 위해 다양한 구조가 사용됩니다. 대부분의 소켓 함수에는 소켓 주소 구조에 대한 포인터가 인수로 필요합니다. 이 장에서 정의 된 구조는 인터넷 프로토콜 제품군과 관련이 있습니다.
양말
첫 번째 구조는 소켓 정보를 보유하는 sockaddr 입니다.
struct sockaddr {
unsigned short sa_family;
char sa_data[14];
};
이것은 대부분의 소켓 함수 호출에서 전달되는 일반적인 소켓 주소 구조입니다. 다음 테이블은 멤버 필드에 대한 설명을 제공합니다-
속성 | 가치 | 기술 |
---|---|---|
sa_family | AF_INET AF_UNIX AF_NS AF_IMPLINK |
주소 패밀리를 나타냅니다. 대부분의 인터넷 기반 응용 프로그램에서 AF_INET을 사용합니다. |
sa_data | 프로토콜 별 주소 | 14 바이트 프로토콜 특정 주소의 내용은 주소 유형에 따라 해석됩니다. 인터넷 제품군의 경우 아래 정의 된 sockaddr_in 구조 로 표시되는 포트 번호 IP 주소를 사용 합니다. |
sockaddr에
소켓의 요소를 참조하는 데 도움이되는 두 번째 구조는 다음과 같습니다.
struct sockaddr_in {
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
다음은 멤버 필드에 대한 설명입니다.
속성 | 가치 | 기술 |
---|---|---|
sa_family | AF_INET AF_UNIX AF_NS AF_IMPLINK |
주소 패밀리를 나타냅니다. 대부분의 인터넷 기반 응용 프로그램에서 AF_INET을 사용합니다. |
sin_port | 서비스 포트 | 네트워크 바이트 순서의 16 비트 포트 번호입니다. |
sin_addr | IP 주소 | 네트워크 바이트 순서의 32 비트 IP 주소입니다. |
sin_zero | 미사용 | 사용되지 않으므로이 값을 NULL로 설정했습니다. |
추가로
이 구조는 위 구조에서만 구조 필드로 사용되며 32 비트 netid / hostid를 보유합니다.
struct in_addr {
unsigned long s_addr;
};
다음은 멤버 필드에 대한 설명입니다.
속성 | 가치 | 기술 |
---|---|---|
s_addr | 서비스 포트 | 네트워크 바이트 순서의 32 비트 IP 주소입니다. |
호스트
이 구조는 호스트와 관련된 정보를 유지하는 데 사용됩니다.
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list
#define h_addr h_addr_list[0]
};
다음은 멤버 필드에 대한 설명입니다.
속성 | 가치 | 기술 |
---|---|---|
h_name | ti.com 등 | 호스트의 공식 이름입니다. 예 : tutorialspoint.com, google.com 등 |
h_aliases | TI | 호스트 이름 별명의 목록을 보유합니다. |
h_addrtype | AF_INET | 주소 계열을 포함하며 인터넷 기반 응용 프로그램의 경우 항상 AF_INET입니다. |
h_length | 4 | 인터넷 주소의 경우 4 인 IP 주소의 길이를 보유합니다. |
h_addr_list | in_addr | 인터넷 주소의 경우 포인터 h_addr_list [0], h_addr_list [1] 등의 배열은 in_addr 구조를 가리 킵니다. |
NOTE − h_addr은 하위 호환성을 유지하기 위해 h_addr_list [0]로 정의됩니다.
서번트
이 특정 구조는 서비스 및 관련 포트와 관련된 정보를 유지하는 데 사용됩니다.
struct servent {
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
다음은 멤버 필드에 대한 설명입니다.
속성 | 가치 | 기술 |
---|---|---|
s_name | http | 이것은 서비스의 공식 명칭입니다. 예 : SMTP, FTP POP3 등 |
s_aliases | 별명 | 서비스 별명 목록을 보유합니다. 대부분의 경우이 값은 NULL로 설정됩니다. |
스포츠 | 80 | 연결된 포트 번호가 있습니다. 예를 들어 HTTP의 경우 80이됩니다. |
s_proto | TCP UDP |
사용되는 프로토콜로 설정됩니다. 인터넷 서비스는 TCP 또는 UDP를 사용하여 제공됩니다. |
소켓 구조에 대한 팁
소켓 주소 구조는 모든 네트워크 프로그램의 필수 부분입니다. 우리는 그것들을 할당하고, 채우고, 그것들에 대한 포인터를 다양한 소켓 함수에 전달합니다. 때때로 우리는 이러한 구조 중 하나에 대한 포인터를 소켓 함수에 전달하고 내용을 채 웁니다.
우리는 항상 이러한 구조를 참조로 전달하고 (즉, 구조 자체가 아니라 구조에 대한 포인터를 전달) 항상 구조의 크기를 다른 인수로 전달합니다.
소켓 함수가 구조체를 채울 때 길이도 참조로 전달되므로 함수가 값을 업데이트 할 수 있습니다. 이를 가치-결과 인수라고합니다.
항상 bzero () 함수에 memset ()을 사용하여 구조 변수를 NULL (즉, '\ 0')으로 설정하십시오. 그렇지 않으면 구조에서 예기치 않은 정크 값을 얻을 수 있습니다.
클라이언트 프로세스가 서버에 연결하려고 할 때 클라이언트는 연결하려는 서버를 식별하는 방법이 있어야합니다. 클라이언트가 서버가있는 호스트의 32 비트 인터넷 주소를 알고 있으면 해당 호스트에 연결할 수 있습니다. 그러나 클라이언트는 해당 호스트에서 실행중인 특정 서버 프로세스를 어떻게 식별합니까?
호스트에서 실행중인 특정 서버 프로세스를 식별하는 문제를 해결하기 위해 TCP와 UDP 모두 잘 알려진 포트 그룹을 정의했습니다.
우리의 목적을 위해 포트는 1024에서 65535 사이의 정수로 정의됩니다. 이는 1024보다 작은 모든 포트 번호가 잘 알려진 것으로 간주되기 때문입니다. 예를 들어 텔넷은 포트 23을 사용하고, http는 80을 사용하고, ftp는 21을 사용하고, 등등.
네트워크 서비스에 대한 포트 할당은 / etc / services 파일에서 찾을 수 있습니다. 자체 서버를 작성하는 경우 서버에 포트를 할당 할 때주의해야합니다. 이 포트가 다른 서버에 할당되지 않도록해야합니다.
일반적으로 5000 이상의 포트 번호를 할당하는 것이 관례입니다. 그러나 포트 번호가 5000 이상인 서버를 작성한 조직이 많이 있습니다. 예를 들어 Yahoo Messenger는 5050에서 실행되고 SIP 서버는 5060에서 실행됩니다.
예제 포트 및 서비스
다음은 서비스 및 관련 포트의 작은 목록입니다. IANA-TCP / IP Port Assignments 에서 최신 인터넷 포트 및 관련 서비스 목록을 찾을 수 있습니다 .
Service | Port Number | Service Description |
에코 | 7 | UDP / TCP는 수신 한 내용을 다시 보냅니다. |
포기 | 9 | UDP / TCP는 입력을 버립니다. |
낮 | 13 | UDP / TCP는 ASCII 시간을 반환합니다. |
청구 | 19 | UDP / TCP는 문자를 반환합니다. |
ftp | 21 | TCP 파일 전송. |
텔넷 | 23 | TCP 원격 로그인. |
smtp | 25 | TCP 이메일. |
낮 | 37 | UDP / TCP는 이진 시간을 반환합니다. |
tftp | 69 | UDP 사소한 파일 전송. |
손가락 | 79 | 사용자에 대한 TCP 정보. |
http | 80 | TCP 월드 와이드 웹. |
로그인 | 513 | TCP 원격 로그인. |
WHO | 513 | 사용자에 대한 UDP 다른 정보. |
Xserver | 6000 | TCP X 창 (NB> 1023). |
포트 및 서비스 기능
Unix는 / etc / services 파일에서 서비스 이름을 가져 오는 다음과 같은 기능을 제공합니다.
struct servent *getservbyname(char *name, char *proto) −이 호출은 서비스 이름과 프로토콜 이름을 가져와 해당 서비스에 해당하는 포트 번호를 반환합니다.
struct servent *getservbyport(int port, char *proto) −이 호출은 포트 번호와 프로토콜 이름을 가져와 해당 서비스 이름을 반환합니다.
각 함수의 반환 값은 다음과 같은 형식의 구조에 대한 포인터입니다.
struct servent {
char *s_name;
char **s_aliases;
int s_port;
char *s_proto;
};
다음은 멤버 필드에 대한 설명입니다.
속성 | 가치 | 기술 |
---|---|---|
s_name | http | 서비스의 공식 명칭입니다. 예 : SMTP, FTP POP3 등 |
s_aliases | 별명 | 서비스 별명 목록을 보유합니다. 대부분의 경우 NULL로 설정됩니다. |
스포츠 | 80 | 연결된 포트 번호가 있습니다. 예를 들어 HTTP의 경우 80이됩니다. |
s_proto | TCP UDP |
사용되는 프로토콜로 설정됩니다. 인터넷 서비스는 TCP 또는 UDP를 사용하여 제공됩니다. |
불행히도 모든 컴퓨터가 동일한 순서로 멀티 바이트 값을 구성하는 바이트를 저장하는 것은 아닙니다. 2 바이트로 구성된 16 비트 인터넷을 고려하십시오. 이 값을 저장하는 방법에는 두 가지가 있습니다.
Little Endian −이 방식에서 하위 바이트는 시작 주소 (A)에 저장되고 상위 바이트는 다음 주소 (A + 1)에 저장됩니다.
Big Endian −이 방식에서 상위 바이트는 시작 주소 (A)에 저장되고 하위 바이트는 다음 주소 (A + 1)에 저장됩니다.
서로 다른 바이트 순서 규칙을 가진 컴퓨터가 서로 통신 할 수 있도록 인터넷 프로토콜은 네트워크를 통해 전송되는 데이터에 대해 표준 바이트 순서 규칙을 지정합니다. 이를 네트워크 바이트 순서라고합니다.
인터넷 소켓 연결을 설정하는 동안 sockaddr_in 구조의 sin_port 및 sin_addr 멤버에있는 데이터가 네트워크 바이트 순서로 표시되는지 확인해야합니다.
바이트 순서 함수
호스트의 내부 표현과 네트워크 바이트 순서 사이에서 데이터를 변환하는 루틴은 다음과 같습니다.
함수 | 기술 |
---|---|
htons () | 호스트 간 짧은 |
htonl () | 긴 네트워크에 호스트 |
ntohl () | 긴 호스트 네트워크 |
ntohs () | 호스트 간 네트워크 단락 |
아래에 이러한 기능에 대한 자세한 내용이 나열되어 있습니다.
unsigned short htons(unsigned short hostshort) −이 함수는 16 비트 (2 바이트) 수량을 호스트 바이트 순서에서 네트워크 바이트 순서로 변환합니다.
unsigned long htonl(unsigned long hostlong) −이 기능은 32 비트 (4 바이트) 수량을 호스트 바이트 순서에서 네트워크 바이트 순서로 변환합니다.
unsigned short ntohs(unsigned short netshort) −이 기능은 16 비트 (2 바이트) 수량을 네트워크 바이트 순서에서 호스트 바이트 순서로 변환합니다.
unsigned long ntohl(unsigned long netlong) −이 기능은 네트워크 바이트 순서에서 호스트 바이트 순서로 32 비트 수량을 변환합니다.
이러한 함수는 매크로이며 결과적으로 호출 프로그램에 변환 소스 코드가 삽입됩니다. 리틀 엔디안 머신에서 코드는 네트워크 바이트 순서로 값을 변경합니다. big-endian 머신에서는 코드가 필요하지 않으므로 삽입되지 않습니다. 함수는 널로 정의됩니다.
호스트 바이트 순서를 결정하는 프로그램
다음 코드를 byteorder.c 파일에 보관 한 다음 컴파일하고 컴퓨터에서 실행합니다.
이 예에서는 2 바이트 값 0x0102를 짧은 정수에 저장 한 다음 두 개의 연속 바이트 인 c [0] (주소 A) 및 c [1] (주소 A + 1)을 확인하여 바이트를 결정합니다. 주문.
#include <stdio.h>
int main(int argc, char **argv) {
union {
short s;
char c[sizeof(short)];
}un;
un.s = 0x0102;
if (sizeof(short) == 2) {
if (un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if (un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
else
printf("unknown\n");
}
else {
printf("sizeof(short) = %d\n", sizeof(short));
}
exit(0);
}
Pentium 시스템에서이 프로그램에 의해 생성 된 출력은 다음과 같습니다.
$> gcc byteorder.c $> ./a.out
little-endian
$>
Unix는 IP 주소를 조작하는 데 도움이되는 다양한 함수 호출을 제공합니다. 이 함수는 ASCII 문자열 (사람이 선호하는 것)과 네트워크 바이트 순서 이진 값 (소켓 주소 구조에 저장된 값) 사이에서 인터넷 주소를 변환합니다.
다음 세 가지 함수 호출은 IPv4 주소 지정에 사용됩니다.
- int inet_aton (const char * strptr, struct in_addr * addrptr)
- in_addr_t inet_addr (const char * strptr)
- char * inet_ntoa (struct in_addr inaddr)
int inet_aton (const char * strptr, struct in_addr * addrptr)
이 함수 호출은 인터넷 표준 점 표기법의 지정된 문자열을 네트워크 주소로 변환하고 제공된 구조에 주소를 저장합니다. 변환 된 주소는 네트워크 바이트 순서 (바이트는 왼쪽에서 오른쪽으로)입니다. 문자열이 유효하면 1을, 오류가 있으면 0을 반환합니다.
다음은 사용 예입니다-
#include <arpa/inet.h>
(...)
int retval;
struct in_addr addrptr
memset(&addrptr, '\0', sizeof(addrptr));
retval = inet_aton("68.178.157.132", &addrptr);
(...)
in_addr_t inet_addr (const char * strptr)
이 함수 호출은 인터넷 표준 점 표기법의 지정된 문자열을 인터넷 주소로 사용하기에 적합한 정수 값으로 변환합니다. 변환 된 주소는 네트워크 바이트 순서 (바이트는 왼쪽에서 오른쪽으로)입니다. 32 비트 바이너리 네트워크 바이트 순서의 IPv4 주소 및 오류시 INADDR_NONE을 반환합니다.
다음은 사용 예입니다-
#include <arpa/inet.h>
(...)
struct sockaddr_in dest;
memset(&dest, '\0', sizeof(dest));
dest.sin_addr.s_addr = inet_addr("68.178.157.132");
(...)
char * inet_ntoa (struct in_addr inaddr)
이 함수 호출은 지정된 인터넷 호스트 주소를 인터넷 표준 점 표기법의 문자열로 변환합니다.
다음은 사용 예입니다-
#include <arpa/inet.h>
(...)
char *ip;
ip = inet_ntoa(dest.sin_addr);
printf("IP Address is: %s\n",ip);
(...)
이 장에서는 완전한 TCP 클라이언트 및 서버를 작성하는 데 필요한 핵심 소켓 기능에 대해 설명합니다.
다음 다이어그램은 완전한 클라이언트 및 서버 상호 작용을 보여줍니다.
소켓 기능
네트워크 I / O를 수행하기 위해 프로세스가 가장 먼저해야 할 일은 소켓 함수를 호출하고 원하는 통신 프로토콜 유형 및 프로토콜 제품군을 지정하는 것입니다.
#include <sys/types.h>
#include <sys/socket.h>
int socket (int family, int type, int protocol);
이 호출은 나중에 시스템 호출에서 사용할 수있는 소켓 설명자를 반환하거나 오류시 -1을 반환합니다.
매개 변수
family − 프로토콜 패밀리를 지정하며 아래 표시된 상수 중 하나입니다 −
가족 | 기술 |
---|---|
AF_INET | IPv4 프로토콜 |
AF_INET6 | IPv6 프로토콜 |
AF_LOCAL | Unix 도메인 프로토콜 |
AF_ROUTE | 라우팅 소켓 |
AF_KEY | 케트 소켓 |
이 장에서는 IPv4를 제외한 다른 프로토콜에 대해서는 다루지 않습니다.
type− 원하는 소켓 종류를 지정합니다. 다음 값 중 하나를 취할 수 있습니다.
유형 | 기술 |
---|---|
SOCK_STREAM | 스트림 소켓 |
SOCK_DGRAM | 데이터 그램 소켓 |
SOCK_SEQPACKET | 시퀀스 된 패킷 소켓 |
SOCK_RAW | 원시 소켓 |
protocol − 인수는 아래에 주어진 특정 프로토콜 유형으로 설정해야합니다. 또는 주어진 패밀리 및 유형 조합에 대한 시스템 기본값을 선택하려면 0을 설정해야합니다.
실험 계획안 | 기술 |
---|---|
IPPROTO_TCP | TCP 전송 프로토콜 |
IPPROTO_UDP | UDP 전송 프로토콜 |
IPPROTO_SCTP | SCTP 전송 프로토콜 |
연결 기능
연결 기능은 TCP 서버와의 연결을 설정하기 위해 TCP 클라이언트에 의해 사용됩니다.
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
이 호출은 서버에 성공적으로 연결되면 0을 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
serv_addr − 대상 IP 주소와 포트를 포함하는 struct sockaddr에 대한 포인터입니다.
addrlen − sizeof (struct sockaddr)로 설정합니다.
바인드 기능
바인드 함수는 소켓에 로컬 프로토콜 주소를 할당합니다. 인터넷 프로토콜에서 프로토콜 주소는 16 비트 TCP 또는 UDP 포트 번호와 함께 32 비트 IPv4 주소 또는 128 비트 IPv6 주소의 조합입니다. 이 함수는 TCP 서버에서만 호출됩니다.
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr,int addrlen);
이 호출은 주소에 성공적으로 바인딩되면 0을 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
my_addr − 로컬 IP 주소와 포트를 포함하는 struct sockaddr에 대한 포인터입니다.
addrlen − sizeof (struct sockaddr)로 설정합니다.
IP 주소와 포트를 자동으로 입력 할 수 있습니다.
포트 번호 값이 0이면 시스템이 임의의 포트를 선택하고 IP 주소에 INADDR_ANY 값이 있으면 서버의 IP 주소가 자동으로 할당됩니다.
server.sin_port = 0;
server.sin_addr.s_addr = INADDR_ANY;
NOTE− 1024 미만의 모든 포트는 예약되어 있습니다. 다른 프로그램에서 사용하는 포트가 아닌 경우 1024 이상 65535 미만의 포트를 설정할 수 있습니다.
는 듣는 기능
듣기 기능은 TCP 서버에 의해 호출과 두 개의 작업을 수행한다 -
listen 함수는 연결되지 않은 소켓을 수동 소켓으로 변환하여 커널이이 소켓으로 향하는 들어오는 연결 요청을 수락해야 함을 나타냅니다.
이 함수의 두 번째 인수는 커널이이 소켓에 대해 대기해야하는 최대 연결 수를 지정합니다.
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd,int backlog);
이 호출은 성공시 0을 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
backlog − 허용 된 연결 수입니다.
는 동의 기능
동의 기능이 완성 된 연결 대기열의 전면에서 다음의 완료 연결을 반환하는 TCP 서버에 의해 호출됩니다. 호출의 서명은 다음과 같습니다-
#include <sys/types.h>
#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
이 호출은 성공시 음수가 아닌 설명자를 반환하고, 그렇지 않으면 오류시 -1을 반환합니다. 반환 된 디스크립터는 클라이언트 소켓 디스크립터로 간주되며 모든 읽기-쓰기 작업은 클라이언트와 통신하기 위해이 디스크립터에서 수행됩니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
cliaddr − 클라이언트 IP 주소와 포트를 포함하는 struct sockaddr에 대한 포인터입니다.
addrlen − sizeof (struct sockaddr)로 설정합니다.
전송 기능
송신 기능은 스트림 소켓 또는 연결된 데이터 그램을 통해 데이터를 전송하기 위해 사용된다. UNCONNECTED 데이터 그램 소켓을 통해 데이터를 보내려면 sendto () 함수를 사용해야합니다.
당신은 사용할 수 있습니다 쓰기 () 데이터를 전송하는 시스템 호출을. 그 서명은 다음과 같습니다-
int send(int sockfd, const void *msg, int len, int flags);
이 호출은 전송 된 바이트 수를 반환합니다. 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
msg − 전송하려는 데이터에 대한 포인터입니다.
len − 전송하려는 데이터의 길이 (바이트 단위)입니다.
flags − 0으로 설정됩니다.
RECV의 기능
RECV의 기능은 스트림 소켓 CONNECTED 또는 데이터 그램을 통해 데이터를 수신하는데 사용된다. UNCONNECTED 데이터 그램 소켓을 통해 데이터를 수신하려면 recvfrom ()을 사용해야합니다.
당신은 사용할 수 있습니다 읽기 () 데이터를 읽을 시스템 호출을. 이 호출은 도우미 함수 장에서 설명합니다.
int recv(int sockfd, void *buf, int len, unsigned int flags);
이 호출은 버퍼로 읽은 바이트 수를 반환합니다. 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
buf − 정보를 읽어 오는 버퍼입니다.
len − 버퍼의 최대 길이입니다.
flags − 0으로 설정됩니다.
sendto를 기능
sendto를 기능이 연결되지 않은 데이터 그램 소켓을 통해 데이터를 전송하는 데 사용됩니다. 그 서명은 다음과 같습니다-
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
이 호출은 전송 된 바이트 수를 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
msg − 전송하려는 데이터에 대한 포인터입니다.
len − 전송하려는 데이터의 길이 (바이트 단위)입니다.
flags − 0으로 설정됩니다.
to − 데이터를 전송해야하는 호스트의 struct sockaddr에 대한 포인터입니다.
tolen − sizeof (struct sockaddr)로 설정합니다.
에 recvfrom의 기능
함수 recvfrom 함수가 연결되지 않은 데이터 그램 소켓으로부터 데이터를 수신하는데 사용된다.
int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);
이 호출은 버퍼로 읽은 바이트 수를 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
buf − 정보를 읽어 오는 버퍼입니다.
len − 버퍼의 최대 길이입니다.
flags − 0으로 설정됩니다.
from − 데이터를 읽어야하는 호스트의 struct sockaddr에 대한 포인터입니다.
fromlen − sizeof (struct sockaddr)로 설정합니다.
가까운 기능
확대 기능은 클라이언트와 서버 간의 통신을 닫기 위해 사용된다. 구문은 다음과 같습니다.
int close( int sockfd );
이 호출은 성공시 0을 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
종료 기능
셧다운 기능이 정상적으로 클라이언트와 서버 사이의 통신을 종료하는 데 사용됩니다. 이 기능은 닫기 기능 에 비해 더 많은 제어를 제공 합니다. 아래의 구문입니다 종료 -
int shutdown(int sockfd, int how);
이 호출은 성공시 0을 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
sockfd − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
how − 숫자 중 하나를 입력하세요 −
0 − 수신이 허용되지 않음을 나타냅니다.
1 − 전송이 허용되지 않음을 나타냅니다.
2− 전송 및 수신이 모두 허용되지 않음을 나타냅니다. how 를 2로 설정 하면 close ()와 같습니다.
선택 기능
선택 기능은 쓰기, 읽기에 대한 준비가 준비 또는 대기 오류 상태가 지정된 파일 설명을 나타냅니다.
애플리케이션이 recv 또는 recvfrom을 호출 하면 해당 소켓에 대한 데이터가 도착할 때까지 차단됩니다. 애플리케이션은 수신 데이터 스트림이 비어있는 동안 다른 유용한 처리를 수행 할 수 있습니다. 또 다른 상황은 애플리케이션이 여러 소켓에서 데이터를 수신하는 경우입니다.
입력 큐에 데이터가없는 소켓에서 recv 또는 recvfrom 을 호출 하면 다른 소켓에서 데이터를 즉시 수신 할 수 없습니다. select 함수 호출은 프로그램이 모든 소켓 핸들을 폴링하여 비 블로킹 읽기 및 쓰기 작업에 사용할 수 있는지 확인함으로써이 문제를 해결합니다.
아래의 구문입니다 선택은 -
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
이 호출은 성공시 0을 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
매개 변수
nfds− 테스트 할 파일 기술자의 범위를 지정합니다. select () 함수는 0에서 nfds-1 범위의 파일 설명자를 테스트합니다.
readfds− 입력시 읽을 준비가 된 파일 설명자를 지정하고 출력시 읽을 준비가 된 파일 설명자를 나타내는 fd_set 유형의 객체를 가리 킵니다 . 빈 집합을 나타 내기 위해 NULL 일 수 있습니다.
writefds− 입력시 쓰기 준비가되었는지 확인할 파일 설명자를 지정하고 출력시 쓰기 준비가 된 파일 설명자를 나타내는 fd_set 유형의 객체를 가리 킵니다 . 빈 집합을 나타 내기 위해 NULL 일 수 있습니다.
exceptfds− 입력시 보류중인 오류 조건에 대해 검사 할 파일 설명자를 지정하고 출력시 보류중인 오류 조건이있는 파일 설명자를 나타내는 fd_set 유형의 객체를 가리 킵니다 . 빈 집합을 나타 내기 위해 NULL 일 수 있습니다.
timeout− 선택 호출이 사용 가능한 I / O 작업에 대해 설명자를 폴링해야하는 시간을 지정하는 timeval 구조체를 가리 킵니다. 시간 초과 값이 0이면 select가 즉시 반환됩니다. 시간 초과 인수가 NULL 인 경우 select는 사용 가능한 I / O 작업에 대해 하나 이상의 파일 / 소켓 핸들이 준비 될 때까지 차단됩니다. 그렇지 않으면 제한 시간이 경과 한 후 또는 최소한 하나의 파일 / 소켓 설명자가 I / O 작업을 수행 할 준비가되면 select 가 반환됩니다.
select의 반환 값은 I / O를 위해 준비된 파일 설명자 세트에 지정된 핸들 수입니다. 시간 제한 필드에 지정된 시간 제한에 도달하면 반환 0을 선택합니다. 파일 설명자 세트를 조작하기 위해 다음 매크로가 존재합니다.
FD_CLR(fd, &fdset)− 파일 설명자 세트 fdset 에서 파일 설명자 fd에 대한 비트를 지 웁니다 .
FD_ISSET(fd, &fdset)− 파일 기술자 fd에 대한 비트 가 fdset가 가리키는 파일 기술자 집합에 설정되어 있으면 0이 아닌 값 을 반환 하고 그렇지 않으면 0을 반환합니다.
FD_SET(fd, &fdset) − 파일 기술자 세트 fdset에서 파일 기술자 fd에 대한 비트를 설정합니다.
FD_ZERO(&fdset) − 모든 파일 설명자에 대해 0 비트를 갖도록 파일 설명자 세트 fdset를 초기화합니다.
이러한 매크로의 동작은 fd 인수가 0보다 작거나 FD_SETSIZE보다 크거나 같은 경우 정의되지 않습니다.
예
fd_set fds;
struct timeval tv;
/* do socket initialization etc.
tv.tv_sec = 1;
tv.tv_usec = 500000;
/* tv now represents 1.5 seconds */
FD_ZERO(&fds);
/* adds sock to the file descriptor set */
FD_SET(sock, &fds);
/* wait 1.5 seconds for any data to be read from any single socket */
select(sock+1, &fds, NULL, NULL, &tv);
if (FD_ISSET(sock, &fds)) {
recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len);
/* do something */
}
else {
/* do something else */
}
이 장에서는 소켓 프로그래밍을 수행하는 동안 사용되는 모든 도우미 함수에 대해 설명합니다. 다른 도우미 기능은 장에 설명되어 있습니다.Ports and Services및 네트워크 Byte Orders.
쓰기 기능
쓰기 버퍼에서 쓰기 닌 nbyte 바이트에 기능 시도가 가리키는 buf에 열린 파일 기술자와 관련된 파일에 fildes .
send () 함수를 사용하여 데이터를 다른 프로세스로 보낼 수도 있습니다 .
#include <unistd.h>
int write(int fildes, const void *buf, int nbyte);
성공적으로 완료되면 write ()는 fildes와 관련된 파일에 실제로 기록 된 바이트 수를 반환합니다. 이 숫자는 nbyte보다 크지 않습니다. 그렇지 않으면 -1이 반환됩니다.
매개 변수
fildes − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
buf − 전송하려는 데이터에 대한 포인터입니다.
nbyte− 쓸 바이트 수입니다. nbyte가 0이면 write ()는 0을 반환하고 파일이 일반 파일이면 다른 결과가 없습니다. 그렇지 않으면 결과가 지정되지 않습니다.
읽기 기능
읽기 버퍼 buf가 가리키는으로 기능을 시도, 버퍼, fildes와 관련된 파일에서 닌 nbyte 바이트를 읽을 수 있습니다.
recv () 함수를 사용 하여 다른 프로세스로 데이터를 읽을 수도 있습니다 .
#include <unistd.h>
int read(int fildes, const void *buf, int nbyte);
성공적으로 완료되면 write ()는 fildes와 관련된 파일에 실제로 기록 된 바이트 수를 반환합니다. 이 숫자는 nbyte보다 크지 않습니다. 그렇지 않으면 -1이 반환됩니다.
매개 변수
fildes − 소켓 함수에 의해 반환 된 소켓 설명자입니다.
buf − 정보를 읽어 오는 버퍼입니다.
nbyte − 읽을 바이트 수입니다.
포크 기능
포크 함수는 새로운 프로세스를 생성한다. 자식 프로세스라고하는 새 프로세스는 호출 프로세스 (부모 프로세스)의 정확한 복사본입니다. 하위 프로세스는 상위 프로세스에서 많은 속성을 상속합니다.
#include <sys/types.h>
#include <unistd.h>
int fork(void);
성공적으로 완료되면 fork ()는 자식 프로세스에 0을 반환하고 자식 프로세스의 프로세스 ID를 부모 프로세스에 반환합니다. 그렇지 않으면 -1이 상위 프로세스로 리턴되고 하위 프로세스가 작성되지 않으며 오류를 표시하기 위해 errno가 설정됩니다.
매개 변수
void − 매개 변수가 필요하지 않음을 의미합니다.
bzero의 기능
bzero의 기능 장소 닌 nbyte의 널 (null) 문자열의 바이트 들 . 이 함수는 모든 소켓 구조를 null 값으로 설정하는 데 사용됩니다.
void bzero(void *s, int nbyte);
이 함수는 아무것도 반환하지 않습니다.
매개 변수
s− 널 바이트로 채워야하는 문자열을 지정합니다. 이것은 소켓 구조 변수에 대한 포인트가 될 것입니다.
nbyte− null 값으로 채워질 바이트 수를 지정합니다. 이것은 소켓 구조의 크기입니다.
bcmp의 기능
bcmp의 함수 바이트 스트링 (S2)에 대해 S1 바이트 문자열을 비교한다. 두 문자열 모두 길이가 nbyte 바이트 인 것으로 간주됩니다.
int bcmp(const void *s1, const void *s2, int nbyte);
이 함수는 두 문자열이 모두 같으면 0을 반환하고 그렇지 않으면 1을 반환합니다. bcmp () 함수는 nbyte가 0 일 때 항상 0을 반환합니다.
매개 변수
s1 − 비교할 첫 번째 문자열을 지정합니다.
s2 − 비교할 두 번째 문자열을 지정합니다.
nbyte − 비교할 바이트 수를 지정합니다.
bcopy 기능
bcopy 함수 복사 닌 nbyte 문자열 (S2)에 문자열 S1에서 바이트. 겹치는 문자열은 올바르게 처리됩니다.
void bcopy(const void *s1, void *s2, int nbyte);
이 함수는 아무것도 반환하지 않습니다.
매개 변수
s1 − 소스 문자열을 지정합니다.
s2v − 목적지 문자열을 지정합니다.
nbyte − 복사 할 바이트 수를 지정합니다.
memset 함수의 기능
memset 함수의 함수도 동일한 방법으로 구조에 설정된 변수를 사용bzero. 아래에 주어진 구문을 살펴보십시오.
void *memset(void *s, int c, int nbyte);
이 함수는 void에 대한 포인터를 반환합니다. 사실, 설정된 메모리에 대한 포인터이며 그에 따라 캐스팅해야합니다.
매개 변수
s − 설정할 소스를 지정합니다.
c − nbyte 자리에 설정할 문자를 지정합니다.
nbyte − 설정할 바이트 수를 지정합니다.
프로세스를 TCP 서버로 만들려면 아래 단계를 따라야합니다.
socket () 시스템 호출 로 소켓을 만듭니다 .
bind () 시스템 호출을 사용하여 소켓을 주소에 바인드 하십시오. 인터넷에있는 서버 소켓의 경우 주소는 호스트 시스템의 포트 번호로 구성됩니다.
listen () 시스템 호출 로 연결을 수신 하십시오.
accept () 시스템 호출 로 연결을 수락합니다 . 이 호출은 일반적으로 클라이언트가 서버에 연결할 때까지 차단됩니다.
read () 및 write () 시스템 호출을 사용하여 데이터를 보내고받습니다 .
이제 이러한 단계를 소스 코드의 형태로 작성하겠습니다. 이 코드를 server.c 파일에 넣고 gcc 컴파일러로 컴파일하십시오 .
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
int main( int argc, char *argv[] ) {
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
/* First call to socket() function */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
/* Initialize socket structure */
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5001;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
/* Now start listening for the clients, here process will
* go in sleep mode and will wait for the incoming connection
*/
listen(sockfd,5);
clilen = sizeof(cli_addr);
/* Accept actual connection from the client */
newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
/* If connection is established then start communicating */
bzero(buffer,256);
n = read( newsockfd,buffer,255 );
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("Here is the message: %s\n",buffer);
/* Write a response to the client */
n = write(newsockfd,"I got your message",18);
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
return 0;
}
여러 연결 처리
서버가 여러 동시 연결을 처리 할 수 있도록 위 코드에서 다음과 같이 변경합니다.
accept 문과 다음 코드를 무한 루프에 넣습니다 .
연결이 설정되면 fork () 를 호출 하여 새 프로세스를 만듭니다.
자식 프로세스는 sockfd 를 닫고 doprocessing 함수를 호출 하여 새 소켓 파일 설명자를 인수로 전달합니다. 두 프로세스가 대화를 완료하면 doprocessing () 반환 으로 알 수 있듯이이 프로세스는 단순히 종료됩니다.
부모 프로세스는 newsockfd를 닫습니다 . 이 코드는 모두 무한 루프에 있으므로 다음 연결을 기다리기 위해 accept 문으로 돌아갑니다.
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
void doprocessing (int sock);
int main( int argc, char *argv[] ) {
int sockfd, newsockfd, portno, clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n, pid;
/* First call to socket() function */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
/* Initialize socket structure */
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = 5001;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
/* Now bind the host address using bind() call.*/
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR on binding");
exit(1);
}
/* Now start listening for the clients, here
* process will go in sleep mode and will wait
* for the incoming connection
*/
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) {
perror("ERROR on accept");
exit(1);
}
/* Create child process */
pid = fork();
if (pid < 0) {
perror("ERROR on fork");
exit(1);
}
if (pid == 0) {
/* This is the client process */
close(sockfd);
doprocessing(newsockfd);
exit(0);
}
else {
close(newsockfd);
}
} /* end of while */
}
다음 코드 시퀀스 는 doprocessing 함수 의 간단한 구현을 보여줍니다 .
void doprocessing (int sock) {
int n;
char buffer[256];
bzero(buffer,256);
n = read(sock,buffer,255);
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("Here is the message: %s\n",buffer);
n = write(sock,"I got your message",18);
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
}
프로세스를 TCP 클라이언트로 만들려면 아래에 주어진 단계를 따라야합니다. & minus;
socket () 시스템 호출 로 소켓을 만듭니다 .
connect () 시스템 호출을 사용하여 소켓을 서버 주소에 연결 합니다.
데이터를 보내고받습니다. 이를 수행하는 방법에는 여러 가지가 있지만 가장 간단한 방법은 read () 및 write () 시스템 호출을 사용하는 것입니다.
이제 이러한 단계를 소스 코드의 형태로 작성하겠습니다. 이 코드를 파일에 넣으십시오.client.c 그리고 그것을 컴파일 gcc 컴파일러.
이 프로그램을 실행 하고 서버의 호스트 이름 과 포트 번호 를 전달 하여 다른 Unix 창에서 이미 실행 한 서버에 연결합니다.
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char *argv[]) {
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
/* Create a socket point */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("ERROR opening socket");
exit(1);
}
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
/* Now connect to the server */
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR connecting");
exit(1);
}
/* Now ask for a message from the user, this message
* will be read by server
*/
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
/* Send message to the server */
n = write(sockfd, buffer, strlen(buffer));
if (n < 0) {
perror("ERROR writing to socket");
exit(1);
}
/* Now read server response */
bzero(buffer,256);
n = read(sockfd, buffer, 255);
if (n < 0) {
perror("ERROR reading from socket");
exit(1);
}
printf("%s\n",buffer);
return 0;
}
다음은 소켓 프로그래밍과 관련된 모든 기능 목록입니다.
포트 및 서비스 기능
Unix는 / etc / services 파일에서 서비스 이름을 가져 오는 다음과 같은 기능을 제공합니다.
struct servent *getservbyname(char *name, char *proto) −이 호출은 서비스 이름과 프로토콜 이름을 가져와 해당 서비스에 해당하는 포트 번호를 반환합니다.
struct servent *getservbyport(int port, char *proto) −이 호출은 포트 번호와 프로토콜 이름을 가져와 해당 서비스 이름을 반환합니다.
바이트 순서 함수
unsigned short htons (unsigned short hostshort) −이 함수는 16 비트 (2 바이트) 수량을 호스트 바이트 순서에서 네트워크 바이트 순서로 변환합니다.
unsigned long htonl (unsigned long hostlong) −이 기능은 32 비트 (4 바이트) 수량을 호스트 바이트 순서에서 네트워크 바이트 순서로 변환합니다.
unsigned short ntohs (unsigned short netshort) −이 기능은 16 비트 (2 바이트) 수량을 네트워크 바이트 순서에서 호스트 바이트 순서로 변환합니다.
unsigned long ntohl (unsigned long netlong) −이 기능은 네트워크 바이트 순서에서 호스트 바이트 순서로 32 비트 수량을 변환합니다.
IP 주소 기능
int inet_aton (const char *strptr, struct in_addr *addrptr)−이 함수 호출은 인터넷 표준 도트 표기법으로 지정된 문자열을 네트워크 주소로 변환하고 제공된 구조에 주소를 저장합니다. 변환 된 주소는 네트워크 바이트 순서 (바이트 순서는 왼쪽에서 오른쪽)입니다. 문자열이 유효하면 1을, 오류가 있으면 0을 반환합니다.
in_addr_t inet_addr (const char *strptr)−이 함수 호출은 인터넷 표준 도트 표기법으로 지정된 문자열을 인터넷 주소로 사용하기에 적합한 정수 값으로 변환합니다. 변환 된 주소는 네트워크 바이트 순서 (바이트는 왼쪽에서 오른쪽으로)입니다. 32 비트 바이너리 네트워크 바이트 순서의 IPv4 주소 및 오류시 INADDR_NONE을 반환합니다.
char *inet_ntoa (struct in_addr inaddr) −이 함수 호출은 지정된 인터넷 호스트 주소를 인터넷 표준 도트 표기법의 문자열로 변환합니다.
소켓 코어 기능
int socket (int family, int type, int protocol) −이 호출은 나중에 시스템 호출에서 사용할 수있는 소켓 설명자를 반환하거나 오류시 -1을 제공합니다.
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen)− 연결 기능은 TCP 클라이언트가 TCP 서버와 연결을 설정하는 데 사용됩니다. 이 호출은 서버에 성공적으로 연결되면 0을 반환하고 그렇지 않으면 -1을 반환합니다.
int bind(int sockfd, struct sockaddr *my_addr,int addrlen)− bind 기능은 소켓에 로컬 프로토콜 주소를 할당합니다. 이 호출은 주소에 성공적으로 바인딩되면 0을 반환하고 그렇지 않으면 -1을 반환합니다.
int listen(int sockfd, int backlog)− listen 기능은 클라이언트 요청을 수신하기 위해 TCP 서버에 의해서만 호출됩니다. 이 호출은 성공하면 0을 반환하고 그렇지 않으면 -1을 반환합니다.
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen)− 클라이언트 요청을 수락하고 실제 연결을 설정하기 위해 TCP 서버가 수락 기능을 호출합니다. 이 호출은 성공시 음수가 아닌 설명자를 반환하고, 그렇지 않으면 -1을 반환합니다.
int send(int sockfd, const void *msg, int len, int flags)− send 함수는 스트림 소켓 또는 CONNECTED 데이터 그램 소켓을 통해 데이터를 전송하는 데 사용됩니다. 이 호출은 전송 된 바이트 수를 반환하고, 그렇지 않으면 -1을 반환합니다.
int recv (int sockfd, void *buf, int len, unsigned int flags)− recv 함수는 스트림 소켓 또는 CONNECTED 데이터 그램 소켓을 통해 데이터를 수신하는 데 사용됩니다. 이 호출은 버퍼로 읽은 바이트 수를 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
int sendto (int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen)− sendto 함수는 UNCONNECTED 데이터 그램 소켓을 통해 데이터를 전송하는 데 사용됩니다. 이 호출은 전송 된 바이트 수를 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
int recvfrom (int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen)− recvfrom 함수는 연결되지 않은 데이터 그램 소켓에서 데이터를 수신하는 데 사용됩니다. 이 호출은 버퍼로 읽은 바이트 수를 반환하고, 그렇지 않으면 오류시 -1을 반환합니다.
int close (int sockfd)− 닫기 기능은 클라이언트와 서버 간의 통신을 종료하는 데 사용됩니다. 이 호출은 성공하면 0을 반환하고 그렇지 않으면 -1을 반환합니다.
int shutdown (int sockfd, int how)− 종료 기능은 클라이언트와 서버 간의 통신을 정상적으로 종료하는 데 사용됩니다. 이 기능은 닫기 기능에 비해 더 많은 제어를 제공합니다. 성공하면 0을, 그렇지 않으면 -1을 반환합니다.
int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) −이 기능은 여러 소켓을 읽거나 쓰는 데 사용됩니다.
소켓 도우미 기능
int write (int fildes, const void *buf, int nbyte)− write 함수는 buf가 가리키는 버퍼에서 열린 파일 설명자인 fildes와 관련된 파일에 nbyte 바이트를 쓰려고 시도합니다. 성공적으로 완료되면 write ()는 fildes와 관련된 파일에 실제로 기록 된 바이트 수를 반환합니다. 이 숫자는 nbyte보다 크지 않습니다. 그렇지 않으면 -1이 반환됩니다.
int read (int fildes, const void *buf, int nbyte)− read 함수는 열린 파일 설명자 fildes와 관련된 파일에서 buf가 가리키는 버퍼로 nbyte 바이트를 읽으려고합니다. 성공적으로 완료되면 write ()는 fildes와 관련된 파일에 실제로 기록 된 바이트 수를 반환합니다. 이 숫자는 nbyte보다 크지 않습니다. 그렇지 않으면 -1이 반환됩니다.
int fork (void)− 포크 기능은 새로운 프로세스를 생성합니다. 자식 프로세스라고하는 새 프로세스는 호출 프로세스 (상위 프로세스)의 정확한 복사본입니다.
void bzero (void *s, int nbyte)− bzero 함수는 문자열 s에 nbyte null 바이트를 배치합니다. 이 함수는 null 값으로 모든 소켓 구조를 설정하는 데 사용됩니다.
int bcmp (const void *s1, const void *s2, int nbyte)− bcmp 함수는 바이트 문자열 s1을 바이트 문자열 s2와 비교합니다. 두 문자열 모두 길이가 nbyte 바이트 인 것으로 간주됩니다.
void bcopy (const void *s1, void *s2, int nbyte)− bcopy 함수는 문자열 s1에서 문자열 s2로 nbyte 바이트를 복사합니다. 겹치는 문자열은 올바르게 처리됩니다.
void *memset(void *s, int c, int nbyte) − memset 함수는 bzero와 같은 방식으로 구조 변수를 설정하는데도 사용됩니다.