Iterative Server
- 각각의 client들에게 한 번씩 서비스를 해주큰 모델
- send ()
- send 함수는 수신자의 Application Layer 까지 데이터를 전달하는 것이 아니라 Transport Layer의 수신버퍼에 데이터를 저장하는 기능 - recv ()
- 수신측은 recv를 통해 원하는 크기만큼의 데이터를 Application Layer로 한 번에 읽어 올 수 있음
- recv 호출 시 수신버퍼에 데이터가 없으면 읽어올 데이터가 올 때까지 기다림
(즉, 송신 측에서 데이터를 보내거나 연결을 해체애야지만 함수 종료 가능)
왼쪽의 그림과 같이 반복적으로 Accept함수를 호출하면, 계속해서 클라이언트의 연결요청을 수락할 수있다. 그러나, 동시에 둘 이상의 클라이언트에게 서비스를 제공할 수 있는 모델은 아니다.
실습
- 순차적으로 3개의 클라이언트에게 에코서비스가 가능한 서버 프로그램을 구현
- Q를 입력받을 때까지 서버에게 에코서비스를 요청하는 클라이언트 구현
Server
//
// Server.c
// socket
//
// Created by Jho on 2020/04/07.
// Copyright © Jho All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void ErrorHandling(char* message);
int main()
{
int hServSock, hClntSock; //서버 및 클라이언트 소켓
char message[BUF_SIZE];
int strLen=0; //수신한 데이터의 길이의 제어변수
socklen_t szClntAddr; //클라이언트 주소 구조체의 바이트 크기
struct sockaddr_in servAddr,clntAddr; // 서버용 소켓, 클라이언트용 소켓
hServSock=socket(PF_INET, SOCK_STREAM, 0);//소켓 개통
if(hServSock<0)
ErrorHandling("socket() error");
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family=AF_INET;//주소체계 지정
servAddr.sin_addr.s_addr=htonl(INADDR_ANY);//IP주소 리드
servAddr.sin_port=htons(35124); //포트 번호 htons->호스트 바이트 순서를 short타입 네트워크바이트 순서로 변환
if(bind(hServSock, (struct sockaddr*) &servAddr, sizeof(servAddr))<0) //주소 할당
ErrorHandling("bind() error");
if(listen(hServSock, 3)<0) //대기 큐 5개
ErrorHandling("listen() error");
szClntAddr=sizeof(struct sockaddr_in);
for (int i=0; i<3;i++)
{
hClntSock=accept(hServSock, (struct sockaddr*)&clntAddr, &szClntAddr); //연결수락
if (hClntSock == -1)
ErrorHandling("accept() error");
else
printf("Connected client %d \n",i+1);
//accept 성공시 클라이언트 소켓 생성
//클라이언트가 소켓을 닫아버리면 NULL(0) 반환
while ((strLen = recv(hClntSock,message, BUF_SIZE, 0))!=0)
{
//send(hClntSock,message, strLen,0);
write(hClntSock,message,strLen);
}
close(hClntSock);
printf("Client %d is disconnected \n",i+1);
}
printf("서버 프로그램을 종료합니다.\n");
close(hServSock);
}
void ErrorHandling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
Client
//
// client.c
// socket
//
// Created by Jho on 2020/04/07.
// Copyright © jho All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void ErrorHandling(char* message);
int main(int argc, char* argv[])
{
int hSocket; //서버에 접속할 소켓
struct sockaddr_in serveraddr; //접속할 서버의 주소
char message[BUF_SIZE];
int str_len;
if(argc!=3)
{
printf("Usage : %s <IP> <port>\n", argv[0]);
exit(1);
}
hSocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(hSocket<0)
ErrorHandling("hSocket() error");
memset(&serveraddr, 0, sizeof(serveraddr)); // 서버쪽 주소 구조체를 0 으로 초기화
serveraddr.sin_family=AF_INET; //IPv4 정보체계저장
serveraddr.sin_addr.s_addr = inet_addr(argv[1]); //주소할당
serveraddr.sin_port=htons(atoi(argv[2])); //포트할당
if(connect(hSocket, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0)
ErrorHandling("connect() error!");
while(1)
{
fputs("전송할 메시지를 입력하세요 (q to quit) : ", stdout);
fgets(message, BUF_SIZE, stdin);
//q를 입력받으면 클라이언트 종료
if(!strcmp(message, "q\n") || !strcmp(message,"Q\n"))
break;
write(hSocket,message,strlen(message)); //소켓을 이용해 메세지 전송
str_len =read(hSocket,message, BUF_SIZE-1); //에코되어 돌아오는 메세지 수신
message[str_len]=0; //보낼때 NULL문자 안보내서, 받았을 때 넣어줘야 한다.
printf("\nMessage from server: %s \n", message);
}
close(hSocket);
return 0;
}
void ErrorHandling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
실행결과