에코클라이언트의 문제점 확인하기
에코 서버의 코드 while((str_len=read(clnt_sock, message, BUF_SIZE)) != 0) write(clnt_sock, message, str_len); |
서버는 데이터의 경계를 구분하지 않고 수신된 데이터를 그대로 전송할 의무만 갖는다. TCP가 본디 데이터의 경계가 없는 프로토콜
이므로, 두 번의 write 함수 호출을 통해서 데이터를 전송하건, 세 번의 write 함수 호출을 통해서 데이터를 전송하건, 문제 되지 않는다.
에코 클라이언트의 코드 write(sock, message, strlen(message)); str_len = read(sock, message, BUF_SIZE - 1); |
반면, 클라이언트는 문장 단위로 데이터를 송수신(BUF_SIZE-1) 하기 때문에, 데이터의 경계를 구분해야한다. 때문에 이와 같은 데이터 송수신 방식은 문제가 된다. TCP의 read & write 함수 호출은 데이터의 경계를 구분하지 않기 때문이다.
에코 클라이언트의 해결책!
str_len = write(sock, message, strlen(message)); recv_len = 0; while(recv_len(str_len) { recv_cnt = read(sock, &message[recv_len], BUF_SIZE-1); if(recv_cnt == -1) error_handling("read() error!"); recv_len += recv_cnt; } message[recv_len] = 0; printf("Message from server : %s", message); |
write 함수호출을 통해서 전송한 데이터의 길이 만큼 읽어 들이기 위한 반복문의 삽입이 필요하다. 이것이 TCP를 기반으로 데이터를 구분지어 읽어 들이는데 부가적으로 필요한 구분이다.
op_server.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 #define OPSZ 4 void error_handling(char *message); int calculate(int opnum, int opnds[], char oprator); int main(int argc, char *argv[]) { int serv_sock, clnt_sock; char opinfo[BUF_SIZE]; int result, opnd_cnt, i; int recv_cnt, recv_len; struct sockaddr_in serv_adr, clnt_adr; socklen_t clnt_adr_sz; if(argc!=2) { printf("Usage : %s <port>\n", argv[0]); exit(1); }
serv_sock=socket(PF_INET, SOCK_STREAM, 0); if(serv_sock==-1) error_handling("socket() error");
memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=htonl(INADDR_ANY); serv_adr.sin_port=htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1) error_handling("bind() error"); if(listen(serv_sock, 5)==-1) error_handling("listen() error"); clnt_adr_sz=sizeof(clnt_adr); for(i=0; i<5; i++) { opnd_cnt=0; clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz); read(clnt_sock, &opnd_cnt, 1);
recv_len=0; while((opnd_cnt*OPSZ+1)>recv_len) { recv_cnt=read(clnt_sock, &opinfo[recv_len], BUF_SIZE-1); recv_len+=recv_cnt; } result=calculate(opnd_cnt, (int*)opinfo, opinfo[recv_len-1]); write(clnt_sock, (char*)&result, sizeof(result)); close(clnt_sock); } close(serv_sock); return 0; } int calculate(int opnum, int opnds[], char op) { int result=opnds[0], i;
switch(op) { case '+': for(i=1; i<opnum; i++) result+=opnds[i]; break; case '-': for(i=1; i<opnum; i++) result-=opnds[i]; break; case '*': for(i=1; i<opnum; i++) result*=opnds[i]; break; } return result; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } |
op_client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 1024 #define RLT_SIZE 4 #define OPSZ 4 void error_handling(char *message); int main(int argc, char *argv[]) { int sock; char opmsg[BUF_SIZE]; int result, opnd_cnt, i; struct sockaddr_in serv_adr; if(argc!=3) { printf("Usage : %s <IP> <port>\n", argv[0]); exit(1); }
sock=socket(PF_INET, SOCK_STREAM, 0); if(sock==-1) error_handling("socket() error");
memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=inet_addr(argv[1]); serv_adr.sin_port=htons(atoi(argv[2]));
if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1) error_handling("connect() error!"); else puts("Connected..........."); fputs("Operand count: ", stdout); scanf("%d", &opnd_cnt); opmsg[0]=(char)opnd_cnt;
for(i=0; i<opnd_cnt; i++) { printf("Operand %d: ", i+1); scanf("%d", (int*)&opmsg[i*OPSZ+1]); } fgetc(stdin); fputs("Operator: ", stdout); scanf("%c", &opmsg[opnd_cnt*OPSZ+1]); write(sock, opmsg, opnd_cnt*OPSZ+2); read(sock, &result, RLT_SIZE);
printf("Operation result: %d \n", result); close(sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } |
'이전것 > 개발' 카테고리의 다른 글
참 좋은 글... 신입부터 2~3년차까지 꼭 알아야 할것!!! (0) | 2017.06.02 |
---|---|
5.2. TCP의 이론적인 이야기 (0) | 2016.12.05 |
해피해킹 딥스위치 설정 (0) | 2016.12.05 |
Vi/Vim 단축키 모음 (0) | 2016.12.02 |
4.3. Iterative 기반의 서버,클라이언트의 구현 (0) | 2016.12.02 |