23.11.20 최초 작성
client
: 서비스를 요청하는 시스템server
: 서비스를 제공하는 시스템 (데이터베이스, 파일서버 등)server
의 용량에 의해 서비스할 수 있는 client
의 수가 제한 됨Peer
의 상황을 고려해 운영해야 함socket
: 컴퓨터 간 통신을 가능하게 하는 end-pointstream socket
tcp
기반의 소켓으로 연결지향적, 신뢰성 있는 데이터 전송 보장datagram socket
udp
기반의 소켓으로 속도가 빠름struct sockaddr{
__SOCKADDR_COMMON (sa_); //주소 체계를 나타내며 TCP/IP 사용 시 AF_INET
char sa_data[14]; //실제 주소 데이터
}
#include <netinet/in.h>
struct cokaddr_in{
short sin_family; //(sa_)에 해당하는 부분
//sa_data[14]를 3부분으로 나눈 것
unsigned short sin_port; //포트 번호 나타냄
struct in_addr sin_addr; //IP 주소 나타냄
char sin_zero[8]; //실제로 사용하지 않으나 0으로 채움
}
struct in_addr{
unsigned long s_addr; //load with init_aton()
}
socket()
: 소켓을 생성하는 함수#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
///
domain : 인터넷을 통해 통신할 지, 같은 시스템 내에서 프로세스 끼리 통신할 지의 여부
type : 데이터의 전송 형태 지정
protocol : 특정 프로토콜을 사용을 지정, 보통 0
Return : 소켓 디스크립터
-1 : Fail
getsockopt()
, setsockopt
: 소켓의 설정 확인 / 변경#include <sys/types.h>
#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, void *optval, socklen_t *optlen);
//optname값 의미
//SOL_SOCKET : socket API level
//IPPROTO_IP : IP protocol level
//IPPROTO_TCP : TCP protocol level
bind
: 소켓에 주소를 할당#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen);
///
sockfd : socket() 함수를 통해 배정받은 디스크립터 번호 serv_sock
addr : IP주소와 PORT번호를 지정한 serv_addr 구조체 (serv_addr은sockaddr_in 이므로 sockaddr 구조체로 변환해준다.)
addrlen : 주소정보를 담은 변수의 길이
Return
(0 : Success, -1 : Fail)
listen()
: 연결요청을 대기int listen(int sock, int backlog);
///
sock : 소켓 디스크립터 번호
backlog : 연결요청을 대기하는 큐의 크기
Return
(0 : Success, -1 : Fail)
accept()
: 연결 요청을 수락int accept(int sock, struct sockaddr*addr, socklen_t *addrlen);
///
sock : 서버소켓(리스닝소켓)의 디스크립터 번호
addr : 대기 큐를 참조해 얻은 클라이언트의 주소정보
addrlen : addr변수의 크기
반환값
0 이상 : Success, 소켓 디스크립터
-1 : Fail
recv
, recvfrom
, recvmsg
: 켓에서 메시지를 수신#include <sys/socket.h>
int recv(int Socket, void *Buffer, size_t Length, int Flags);
ssize_t recvfrom(int Socket, void *Buffer, size_t Length, int Flags, struct sockaddr *From, socklen_t * FromLength);
int recvmsg(int Socket, struct msghdr Message, int Flags);
///
Socket : 소켓 디스크립터
Buffer : 메시지가 저장될 공간의 주소
Length : Buffer의 길이
Flag : 메시지 수신을 제어하는 값
From : 메시지를 보내는 주소가 들어간 구조체
FromLegnth : 송신자의 길이 지정
Message : msghdr 구조의 주소
Flag 옵션
MSG_OOB : 대역 외 데이터를 처리
MSG_PEEK : 수신 데이터에 있는 Peeks를 참조, 데이터는 읽지 않은 것으로 계속 처리되며 다음 수신 기능을 하는 함수에게 읽혀짐
MSG_WAITALL : 요청된 바이트 수를 읽을 때까지 함수가 리턴되지 않도록 요청
Return : 수신한 메시지의 (recv, recvfrom : 바이트 수, recvmsg : 메시지의 수)
-1 : Fail
send
, sendto
, sendmsg
: 소켓에서 메시지를 보냄#include <sys/types.h>
#include <sys/socketvar.h>
#include <sys/socket.h>
int send(int Socket, const void *Message, size_t Length, int Flags);
int sendto(int Socket, const void *Message, size_t Length, int Flags, const struct sockaddr *To, socklen_t * ToLength)
int sendmsg(int Socket, struct msghdr Message, int Flags);
///
Socket : 소켓 디스크립터
Message : 메시지가 저장된 공간의 주소
Length : Message의 길이
Flag : 메시지 송신을 제어하는 값
To : 메시지의 수신자 주소를 지정
ToLength : 수신자 주소의 크기를 지정
Flag 옵션
MSG_OOB : SOCK_STREAM 통신을 지원하는 소켓의 대역 외 데이터를 처리
MSG_DONTROUTE : 라우팅 테이블을 사용하지 않고 전송
MSG_MPEG2 : 이 블록이 MPEG2 블록임을 표시
Return : 송신한 메시지의 (send, sendto : 바이트 수, sendmsg : 메시지의 수)
-1 : Fail
inet_aton()
: Dotted-Decimal Notation
형식을 네트워크 바이트 오더(Big Endian 32bit)
값으로 변환struct sockaddr
구조체의 struct in_addr
에 IP 주소를 입력할 때 사용#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
///
cp : Dotted-Decimal Notation형식의 IP 주소
inp : IP 주소를 입력할 in_addr 구조체
Return
0이 아닌 값 : Success
0 : Fail
inet_pton()
: 네트워크 바이트 오더(Big Endian 32bit)
형식을 Dotted-Decimal Notation
값으로 변환#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
///
af : address family를 지정
src : 문자열 형태의 IP주소
dst : src를 binary형태로 변환 후 복사된 메모리의 포인터를 저장할 공간
Return
1 : Success
0 : address family의 유효한 값이 아닌 경우
-1 : Fail
inet_netof
, inet_ntoa
, inet_lnaof
: unsigned long
형식의 IP 주소를 Dotted-Decimal Notation
형식으로 변환#include <sys/socket.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_netof(struct in_addr InternetAddr);
int inet_Inaof (struct in_addr InternetAddr);
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa (struct in_addr InternetAddr);
///
InternetAddr : unsigned long형식의 IP 주소
Return : 인터넷 주소 (lnaof의 경우 Class A, B, C에 따른 리턴값 다름)
-1 : Fail
inet_network
, inet_addr
: Dotted-Decimal Notation
형식의 IP 주소를 unsiged long
값으로 변환#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_network (register const char *CharString);
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr (register const char *CharString);
///
CharString : Dotted-Decimal Notation형식의 IP 주소
Return
unsigned Integer : Success
-1 : Fail
inet_makeaddr
: 네트워크 IP
및 호스트 IP
를 기반으로 주소가 포함된 in_addr
구조체를 리턴#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
struct in_addr inet_makeaddr (int Net, int LocalNetAddr);
///
Net : 인터넷 IP
LocalNetAddr : 로컬 네트워크 IP
Return : IP 주소가 포함도니 in_addr 구조체
-1 : Fail
htonl
, htons
, ntohl
, ntohs
: 호스트와 네트워크에 맞는 형식으로 변경#include <netinet/in.h>
uint32_t htonl(uint32_t long data); //host형식을 network short로
uint16_t htons(uint16_t char data); //host형식을 network long으로
uint32_t ntohl(uint32_t long data); //network형식을 host short로
uint16_t ntohs(uint16_t char data); //network형식을 host long으로
etc/hosts/
파일에 위치하며 아래 함수들은 해당 값과 관련된 것들임gethostbyname
, gethostbyaddr
: 호스트 이름/ IP 주소를 바탕으로 네트워크 데이터베이스에서 값을 찾아 hostent
구조체에 저장해 포인터를 반환 gethostent
, sethostent
, endhostent
: 네트워크 호스트 파일을 open/읽는 위치 결정/닫는 함수 (libnsl.so
include 해줘야 함)#include <netdb.h>
struct hostent *gethostbyname(char *Name);
struct hostent *gethostbyaddr(const void *Address, size_t Length, int Type);
struct hostent *gethostent(void); //호스트명과 IP 주소를 읽어서 hostent 구조체에 저장하고 그 주소를 리턴
int sethostent(int StayOpen); //IP 주소 데이터베이스의 현재 읽기 위치를 시작 부분으로 재설정
int endhostent(void); //IP 주소 데이터베이스를 닫음
///
Name : 호스트 이름
Address : 호스트 주소
Length : 호스트 주소 길이
Type : 호스트 주소 도메인 유형 (AF_INET, AF_INET6)
StayOpen : IP 주소 데이터베이스를 열어둘지 여부를 나타내는 값
(0이 아니면 데이터베이스가 열린 채로 둠)
struct hostent{
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
/*
* h_name : 호스트명 저장
* h_aliases : 호스트를 가리키는 다른 이름들 저장
* h_addrtype : 호스트 주소의 형식 지정
* h_length : 주소의 길이 저장
* h_addr_list : 해당 호스트의 주소 목록을 저장
*/
Return
gethostbyname, gethostbyaddr, gethostent : 해당 구조체를 반환
NULL : Fail
sethostent endhostent
0 : Success, -1 : Fail
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[]) {
struct sockaddr_in server, remote;
int request_sock, new_sock; //요청을 받는 소켓, 요청을 받고 실제 연결을 넘겨받을 소켓
int bytesread, addrlen;
int i;
char buf[BUFSIZ];
// port 번호를 argv에 받아야 함
if (argc != 2) {
(void) fprintf(stderr,"usage: %s port\n", argv[0]);
exit(1);
}
// 소켓 생성
if ((request_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
exit(1);
}
// 소켓과 메모리상의 주소를 바인드 해 줌
memset((void *) &server, 0, sizeof (server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
printf("%d\n", INADDR_ANY);
server.sin_port = htons((u_short)atoi(argv[1])); //호스트 타입으로 지정된 short 타입을
//네트워크에서 사용하는 형식으로 바꿔 줌
if (bind(request_sock, (struct sockaddr *)&server, sizeof (server)) < 0) {
perror("bind");
exit(1);
}
//받은 메시지를 저장하는 queue의 크기 지정
if (listen(request_sock, SOMAXCONN) < 0) {
perror("listen");
exit(1);
}
//요청 대기 &
for (;;) {
addrlen = sizeof(remote);
//연결 요청 올 때 까지 대기
new_sock = accept(request_sock,
(struct sockaddr *)&remote, (socklen_t *)&addrlen);
if (new_sock < 0) {
perror("accept");
exit(1);
}
printf("connection from host %s, port %d, socket %d\n",
inet_ntoa(remote.sin_addr), ntohs(remote.sin_port), new_sock);
u_short ntohs(u_short netshort);
// inet_ntoa : 네트워크 바이트 순서의 32비트 값을 Dotted-Demical-Notation의
// 주소값으로 변환해 문자열 포인터를 반환
// ntohs : 네트워크 바이트 정렬 방식의 2바이트 데이터를
// 호스트 바이트 정렬 방식으로 변환
//소켓에서 데이터를 읽어서 내용을 대문자로 바꿔 다시 보내 줌
for (;;) {
bytesread = read(new_sock, buf, sizeof (buf) - 1);
if (bytesread<=0) {
printf("server: end of file on %d\n", new_sock);
if (close(new_sock))
perror("close");
break;
}
buf[bytesread] = '\0';
printf("%s: %d bytes from %d: %s\n",
argv[0], bytesread, new_sock, buf);
for(i = 0; i < bytesread; i++)
buf[i] = toupper(buf[i]);
/* echo it back */
if (write(new_sock, buf, bytesread) != bytesread)
perror("echo");
}
}
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
int main(int argc, char *argv[])
{
struct hostent *hostp; //호스트의 정보 저장
struct sockaddr_in server; //서버의 주소를 저장
int sock; //socket 디스크립터 저장
char buf[BUFSIZ]; //보낼 데이터
int bytesread; //읽은 데이터의 크기 저장
//호스트 IP or Domain 이름, 호스트 이름, 포트번호 인자로
if(argc != 3)
{
(void) fprintf(stderr,"usage: %s host port\n", argv[0]);
exit(1);
}
//소켓 생성
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket");
exit(1);
}
if ((hostp = gethostbyname(argv[1])) == 0) {
fprintf(stderr,"%s: unknown host\n",argv[2]);
exit(1);
}
// 소켓과 메모리상의 주소를 바인드 해 줌
memset((void *) &server, 0, sizeof (server));
server.sin_family = AF_INET;
memcpy((void *) &server.sin_addr, hostp->h_addr, hostp->h_length);
server.sin_port = htons((u_short)atoi(argv[2]));
//서버와 연결 생성
if (connect(sock, (struct sockaddr *)&server, sizeof (server)) < 0) {
(void) close(sock);
fprintf(stderr, "connect");
exit(1);
}
for (;;) {
/* data from keyboard */
if (!fgets(buf, sizeof buf, stdin)) {
close(sock);
exit(0);
}
// 소켓에 데이터를 쓰고 서버에 전송
if (write(sock, buf, strlen(buf)) < 0) {
perror("write");
exit(1);
}
// 서버로부터 온 데이터 읽음
bytesread = read(sock, buf, sizeof buf - 1);
buf[bytesread] = '\0';
printf("%s: got %d bytes: %s\n", argv[0], bytesread, buf);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
int sockid, nread, addrlen;
int i;
struct sockaddr_in my_addr, client_addr;
char msg[50];
if(argc != 2)
{
printf("%s port\n", argv[0]);
return 0;
}
//소켓 생성
printf("Server: creating socket\n");
if ( (sockid = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{ printf("Server: socket error: %d\n",errno); exit(0); }
// 소켓과 메모리상의 주소를 바인드 해 줌
printf("Server: binding my local socket\n");
memset((char *) &my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(atoi(argv[1]));
if ( (bind(sockid, (struct sockaddr *) &my_addr,
sizeof(my_addr)) < 0) )
{ printf("Server: bind fail: %d\n",errno); exit(0); }
while(1) {
printf("Server: starting blocking message read\n");
addrlen = sizeof(client_addr);
//클라이언트에서 데이터를 읽음 (UDP)
nread = recvfrom(sockid, msg, sizeof (msg) -1 ,0,
(struct sockaddr *) &client_addr, (socklen_t *)&addrlen);
msg[nread] = '\0';
printf("Server: retrun code from read is %d\n",nread);
printf("msg from host %s, port %d, socket %d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), sockid);
if (nread >0) printf("Server: message is: %s\n",msg);
for(i = 0; i < nread; i++)
msg[i] = toupper(msg[i]);
sendto(sockid, msg, strlen(msg), 0, (struct sockaddr *) &client_addr, sizeof(client_addr));
}
close(sockid);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int sockid, retcode;
int nread, addrlen;
struct hostent *hostp;
struct sockaddr_in my_addr, server_addr;
char msg[128];
if(argc != 4)
{
printf("%s myport serveraddr serverport\n", argv[0]);
return 0;
}
// 소켓 생성
printf("Client: creating socket\n");
if ( (sockid = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{ printf("Client: socket failed: %d\n",errno); exit(0); }
// 소켓과 메모리상의 주소를 바인드 해 줌
printf("Client: binding my local socket\n");
memset((char *) &my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(atoi(argv[1]));
if ( (bind(sockid, (struct sockaddr *) &my_addr,
sizeof(my_addr)) < 0) )
{ printf("Client: bind fail: %d\n",errno); exit(0); }
if ((hostp = gethostbyname(argv[2])) == 0) {
fprintf(stderr,"%s: unknown host\n",argv[2]);
exit(1);
}
printf("Client: creating addr structure for server\n");
bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[2]);
memcpy((void *) &server_addr.sin_addr, hostp->h_addr, hostp->h_length);
server_addr.sin_port = htons((u_short)atoi(argv[3]));
printf("Client: initializing message and sending\n");
/* data from keyboard */
if (!fgets(msg, sizeof (msg) - 1, stdin)) {
close(sockid);
exit(0);
}
//서버에 데이터를 보냄
retcode = sendto(sockid,msg,strlen(msg),0,(struct sockaddr *) &server_addr,
sizeof(server_addr));
if (retcode <= -1)
{printf("client: sendto failed: %d\n",errno); exit(0); }
printf("Client: Successful\n");
addrlen = sizeof(server_addr);
nread = recvfrom(sockid, msg, sizeof (msg) - 1 ,0,
(struct sockaddr *) &server_addr, (socklen_t *)&addrlen);
printf("Msg: %s\n", msg);
/* close socket */
close(sockid);
}