전 에코 클라이언트의 소스코드에서 받아오는 부분의 입출력 문장은 다음과 같다.
wrtie(sock, messag, strlen(message));
str_len = read(sock, message, BUF_SIZE -1);
이 부분에서 문제가 있다고 볼 수 있는데 이 문장의 의도는 read 함수호출을 통해서 자신이 전송한 문자열 데이터를 한방에 수신하기를 원하고 있다는 것이다. 이를 이치에 맞게 조정하려면 문자열 데이터가 전송되었을 때 이를 모두 읽어서 출력되도록 바꾸면 된다.
echo_client2.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
void error_handling(char * message);
int main(int argc, char* argv[])
{
int sock;
char message[BUF_SIZE];
int str_len, recv_len, recv_cnt;
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.............");
}
while(1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if(!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
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);
}
close(sock);
return 0;
}
void error_handling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
입출력 부분에서 while문을 삽입하며 문자열을 받은 길이가 자신이 보낸 문자열 만큼 길어졌을 때까지 반복을 진행하여 문자열 데이터가 모두 받았을 때, 문자열을 출력하도록 만들고 있다.
op_client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.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;
struct sockaddr_in serv_adr;
char opmsg[BUF_SIZE];
int result, opnd_cnt, i;
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(argv[2]);
if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
error_handling("conect error!");
else
printf("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);
scnaf("%c", &opmsg[opnd_cnt*OPSZ+1]);
write(sock, opmsg, opnd_cnt*OPSZ+2);
read(sock, &result, RLT_SIZE);
printf("Operation result: %d", result);
close(sock);
return 0;
}
void error_handling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
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(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(int 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]);
wrtie(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(int i = 1; i<opnum; i++)
result += opnds[i];
break;
case '-':
for(int i = 1; i<opnum; i++)
result -= opnds[i];
break;
case '*':
for(int i = 1; i<opnum; i++)
result *= opnds[i];
break;
}
return result;
}
void error_handling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
그림에서 보는 것과 같이, write 함수가 호출되면 출력버퍼라는 곳에 데이터가 전달되고, 상황에 맞게 데이터를 입력버퍼로 전송한다. 입출력 버퍼의 특성은 다음과 같다.
TC가 데이터의 흐름까지 컨트롤 해주어 입력버퍼의 크기를 초과하는 분량의 데이터 전송은 발생시키지 않는다.
Three way handshaking
그림은 다음과 같은 순서로 이루어진다.
(SYN) SEQ: 1000, ACK: -
"내가 지금 보내는 이 패킷에 1000이라는 번호를 부여하니, 잘 받았다면 다음에는 1001번 패킷을 전달하라고 내게 말해달라!"
(SYN+ACK) SEQ: 2000, ACK: 1001
"내가 지금 보내는 이 패킷에 2000이라는 번호를 부여하니 잘 받았다면 다음에는 2001번 패킷을 전달하라고 내게 말해달라!" (SEQ 2000과 관련)
"좀 전에 전송한 SEQ가 1000인 패킷은 잘 받았으니, 다음 번에는 SEQ가 1001인 패킷을 전송하기 바란다!" (ACK 1001과 관련)
(ACK) SEQ: 1001, ACK: 2001
"좀 전에 전송한 SEQ가 2000인 패킷은 잘 받았으니, 다음 번에는 SEQ가 2001인 패킷을 전송하기 바란다!"
ACK 번호 -> SEQ 번호 + 전송된 바이트 크기 + 1
TCP 소켓은 중간에 문제가 발생하여 SEQ에 대한 ACK메시지를 받지 못하였을 때 데이터의 손실에 대한 재전송을 위해 타이머를 동작시켜 Time out! 되었을 때 패킷을 재전송한다.
Four-way handshaking
op_client_win.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#define BUF_SIZE 1024
#define RLT_SIZE 4
#define OPSZ 4
void ErrorHandling(char* message);
int main(int argc, char* argv[])
{
WSADATA wsaData;
SOCKET hSocket;
char opmsg[BUF_SIZE];
int result, opndCnt, i;
SOCKADDR_IN servAdr;
if (argc != 3)
{
printf("Usage : %s <IP> <port> \n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHandling("WSAStartup() error!");
hSocket = socket(PF_INET, SOCK_STREAM, 0);
if (hSocket == INVALID_SOCKET)
ErrorHandling("socket() error!");
memset(&servAdr, 0, sizeof(servAdr));
servAdr.sin_family = AF_INET;
servAdr.sin_addr.s_addr = inet_addr(argv[1]);
servAdr.sin_port = htons(atoi(argv[2]));
if (connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)
ErrorHandling("connect() error!");
else
puts("Connected..........");
fputs("Operand count: ", stdout);
scanf("%d", &opndCnt);
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);
scnaf("%c", &opmsg[opndCnt * OPSZ + 1]);
send(hSocket, opmsg, opndCnt * OPSZ + 2, 0);
recv(hSocket, &result, RLT_SIZE, 0);
printf("Operation result: %d \n", result);
closesocket(hSocket);
WSACleanup();
return 0;
}
void ErrorHandling(char* message)
{
fputs(message, stderr);
fputc("\n", stderr);
exit(1);
}
op_server_win.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#define BUF_SIZE 1024
#define OPSZ 4
void ErrorHandling(char* message);
int calculate(int opnum, int opnds[], char oprator);
int main(int argc, char* argv[])
{
WSADATA wsaData;
SOCKET hServSock, hClntSock;
char opinfo[BUF_SIZE];
int result, opndCnt, i;
int recvCnt, recvLen;
SOCKADDR_IN servAdr, clntAdr;
int clntAdrSize;
if (argc != 2)
{
printf("Usage: %s <port> \n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHandling("WSAStartup() error!");
hServSock = socket(PF_INET, SOCK_STREAM, 0);
if (hServSock == INVALID_SOCKET)
ErrorHandling("socket() error!");
memset(&servAdr, 0, sizeof(servAdr));
servAdr.sin_family = AF_INET;
servAdr.sin_addr.s_addr = htonl(INADDR_ANY);
servAdr.sin_port = htons(argv[1]);
if (bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)
ErrorHandling("bind() error!");
if (listen(hServSock, 5) == SOCKET_ERROR)
ErrorHandling("listen() error!");
clntAdrSize = sizeof(clntAdr);
for (i = 0; i < 5; i++)
{
opndCnt = 0;
hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, clntAdrSize);
recv(hClntSock, &opndCnt, 1, 0);
recvLen = 0;
while ((opndCnt * OPSZ + 1) > recvLen)
{
recvCnt += recv(hClntSock, &opinfo[recvLen], BUF_SIZE - 1, 0);
recvLen += recvCnt;
}
result = calculate(opndCnt, (int*)opinfo, opinfo[recvLen - 1]);
send(hClntSock, (char*)&result, sizeof(result), 0);
closesocket(hClntSock);
}
closesocket(hServSock);
WSACleanup();
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 ErrorHandling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}