어플리케이선 프로토콜
- 소켓 프로그래밍에서 서버와 클라이언트 사이에서의 데이터 송수신 명세가 바로 프로토콜
- 프로토콜은 명확히 정의해야한다.
계산기 소켓 통신
- 클라이언트는 서버에 접속하자마자 피연사자의 개수정보를 1바이트 정수형태로 전달
- 클라이언트가 서버에 전달하는 정수 하나는 4byte로 표현
- 정수를 전달한 다음에는 연산의 종류. 연산정보는 1바이트로 전달
- 서버는 연산결과를 4바이트 정수의 형태로 클라이언트에게 전달
- 연산결과를 얻은 클라이언트는 서버와 연결을 종료
Iterative 소켓 통신을 이용하여 서버가 클라이언트에게 순차적으로 계산기 서비스를 제공하도록 구현해보자
Server
//
// Server.c
// socket
//
// Created by Jho on 2020/04/13.
// 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
#define OPSZ 4
void ErrorHandling(char* message);
int calculate(int opnum, int opends[], char op);
int main()
{
int hServSock, hClntSock; //서버 및 클라이언트 소켓
char message[BUF_SIZE];
int i;
int result, opendCnt, recv_cnt,recv_len; //수신한 데이터의 길이의 제어변수
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(i = 0; i < 5; i++)
{
hClntSock = accept(hServSock, (struct sockaddr*)&clntAddr, &szClntAddr);
if(hClntSock == -1)
ErrorHandling("accept() error");
else
printf("Connected client %d \n", i+1);
int flag;
//client에게 지속적인 서비를 해주기 위한 while문
while(1)
{
flag = 0; //flag 를 이용하여 while문 종료조건 부여
opendCnt = 0; // 피연산자의 수를 받아 오기 위한 int
recv_len = 0; // buffer로 부터 읽어온 길이를 판단하기 위한 int
//buffer로 부터 피연산자의 수만 얻기위해 1byte만 읽음
read(hClntSock, &opendCnt, 1);
// client로부터 요청 데이터의 크기는 4bytes(int)+피연산자수+1(연산자)
while((opendCnt * OPSZ + 1) > recv_len)
{
recv_cnt = recv(hClntSock, &message[recv_len], BUF_SIZE,0);
recv_len += recv_cnt;
if(recv_cnt == -1 || recv_cnt ==0)
{
flag = 1;
break;
}
}
if (flag ==1)
break;
//client가 비정상 졸료 또는 close 호출했다면 while을 완전 종료
//수신한 데이터를 통해 연산함수 호출하고 결과값 전
result = calculate(opendCnt, (int*)message, message[recv_len - 1]);
write(hClntSock, (char*)&result, sizeof(result));
printf("result : %d \n",result);
}
printf("Client %d is disconnected \n", i+1);
close(hClntSock);
}
close(hServSock);
}
int calculate(int opnum, int opnds[], char op)
{
int output = opnds[0], i;
switch(op)
{
case '+':
for(i = 1; i < opnum; i++) output += opnds[i];
break;
case '-':
for(i = 1; i < opnum; i++) output -= opnds[i];
break;
case '*':
for(i = 1; i < opnum; i++) output *= opnds[i];
break;
}
return output;
}
void ErrorHandling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
Client
//
// client.c
// socket
//
// Created by Jho on 2020/04/13.
// 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
#define RLT_SIZE 4
#define OPSZ 4
void ErrorHandling(char* message);
int main(int argc, char* argv[])
{
int hSocket; //서버에 접속할 소켓
struct sockaddr_in serveraddr; //접속할 서버의 주소
char opmsg[BUF_SIZE];
int result, opndCnt, i;
unsigned char flag;
int recv_len, recv_cnt;
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!");
else
puts("Connected.............");
while(1)
{
flag =' ';
fputs("Operand count (Q to quit) : ",stdout);
scanf(" %c",&flag);
if(flag == 'q' || flag =='Q')
break;
else
opndCnt = (int)flag-48;
opmsg[0] = (char)opndCnt;
//피연산자 개수만큼 숫자를 입력
for(i = 0; i < opndCnt; i++)
{
printf("Operand %d : ", i+1);
scanf("%d", (int*)&opmsg[i*OPSZ+1]);
}
fgetc(stdin);
fputs("Operator : ", stdout); //연산 기호 입력
scanf("%c", &opmsg[opndCnt*OPSZ+1]); //전체 데이터 전송
write(hSocket, opmsg, opndCnt*OPSZ+2);
read(hSocket, &result, RLT_SIZE);
//client로 부터 오는 데이터는 연산 결과인 4byte int형
while(RLT_SIZE > recv_len)
{
recv_cnt = recv(hSocket,&result,RLT_SIZE,0)
if(recv_cnt == -1)
ErrorHandling("read() error!");
recv_len += recv_cnt;
}
printf("Operation result %d\n", result);
}
close(hSocket);
return 0;
}
void ErrorHandling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
실행결과