socket
socket echo server, client인 데 소스 봐주시면 감사하겠습니다.
메시지를 보내면 서버에서 받을 때 grabage 값이 있고
버퍼 사이즈보다 많은 데이터 전송하고 수신 시 값이 잘리는 오류가 있습니다.
이럴 때 어떻게 해결하면 되는지 봐주시면 감사하겠습니다.
echoserver.c
#include "libDefine.h"
#include
int main(int argc, char *argv[])
{
/* signal 처리 */
signal(SIGINT, signalHandler);
int nServerSock = -1;
int nClientSock = -1;
int nPort = -1; /* server socket, client socket */
char pMsg[SIZEBUFFER]; /* 수신용 버퍼 */
int nSocketLen = 1;
struct sockaddr_in stClientAddr;
unsigned int uClientAddrSize = sizeof(struct sockaddr_in);
if (argc != 2)
{
printf("Usage : %s \n", argv[0]);
exit(1);
}
nPort = atoi(argv[1]);
printf("서버 연결 중입니다.\n");
nServerSock = serversSocket(nPort);
if (nServerSock == -1)
{
printf("error = %s\n", strerror(errno));
errHandle("socket() error");
}
//uClientAddrSize = sizeof(stClientAddr); /* 클라이언트 주소 구조체의 크기 */
while (1)
{
nClientSock = accept(nServerSock, (struct sockaddr*) &stClientAddr, &uClientAddrSize);
if (nClientSock == -1)
{
printf("error = %s\n", strerror(errno));
errHandle((char*) "accept() 서버로부터 연결 요청 수락이 되지 않았습니다.");
close(nServerSock);
}
else
{
printf("접속한 클라이언트 IP : %s\n", inet_ntoa(stClientAddr.sin_addr));
printf("accept() 클라이언트의 연결 요청 수락되었습니다.\n");
}
while ((nSocketLen = read(nClientSock, pMsg, SIZEBUFFER)) != 0)
{
/*
if ((nSocketLen = read(nClientSock, pMsg, SIZEBUFFER)) == -1)
{
errHandle("read() 되지 않았습니다.");
printf("error = %s\n", strerror(errno));
}
*/
printf("클라이언트로 받은 메시지: %s\n", pMsg);
write(nClientSock, pMsg, nSocketLen);
if(nSocketLen == -1)
{
errHandle("read 되지 않았습니다.");
}
}
//close(nClientSock);
printf("연결이 끊어졌습니다.\n");
}
close(nClientSock);
return 0;
}
echoclient
#include "libDefine.h"
/*
jmp_buf x;
void jmp()
{
longjmp(x, 5);
}
*/
int main(int argc, char *argv[])
{
signal(SIGINT, signalHandler);
int nClientSock = -1;
int nPort = 0;
char* pAddr;
char pMsg[SIZEBUFFER + 0x01]; /* 서버에 보낼 메시지를 저자알 문자열 버퍼 */
int nStrLen = 0; /* 송수신 메시지의 문자열 길이 */
/* port */
if (argc != 3)
{
printf("usage : %s \n", argv[0]);
exit(1);
}
pAddr = argv[1];
nPort = atoi(argv[2]);
nClientSock = clientSocket(nPort, pAddr);
if(nClientSock == -1)
{
printf("error = %s\n", strerror(errno));
errHandle("socket() error");
}
while (1)
{
//pMsg = NULL;
fputs("입력 메시지(Q는 종료) : ", stdout);
fgets(pMsg, SIZEBUFFER, stdin);
if (!strcasecmp(pMsg, "q\n"))
{
break;
}
write(nClientSock, pMsg, strlen(pMsg));
/*
if(write(nClientSock, pMsg, strlen(pMsg)) != 0)
{
//errHandle("Write() error");
write(nClientSock, pMsg, strlen(pMsg));
}
else
{
errHandle("Write() error");
//write(nClientSock, pMsg, strlen(pMsg));
}
*/
/*
if((read(nClientSock, pMsg, SIZEBUFFER)) != 0)
{
nStrLen = read(nClientSock, pMsg, SIZEBUFFER);
errHandle("read 되지 않았습니다.");
printf("error = %s\n", strerror(errno));
}
else if((read(nClientSock, pMsg, SIZEBUFFER)) == -1)
{
errHandle("read 되지 않았습니다.");
}
*/
nStrLen = read(nClientSock, pMsg, SIZEBUFFER - 1);
/*
if((iStrLen = setjmp(x)) <= 1025)
{
jmp();
}
else
{
switch(iStrLen >= 1025)
{
default:
fprintf(stdout, "error code = %d\n", iStrLen);
break;
}
}
*/
pMsg[nStrLen] = 0;
printf("서버로 받은 메시지 : %s\n", pMsg);
}
close(nClientSock);
return 0;
}
libDefine 헤더, 함수있는 파일
#include "libDefine.h"
int serversSocket(int port)
{
int nServerSock = -1; /* server socket */
struct sockaddr_in stServerAddr; /* 서버용 소켓(accept용) */
int nOptions = 1;
/* TCP 통신용 서버 소켓 생성 */
nServerSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (nServerSock == -1)
{
printf("error = %s\n", strerror(errno));
errHandle((char*) "socket() error");
}
else
{
printf("socket() TCP 통신용 서버 소켓이 생성되었습니다.\n");
}
memset(&stServerAddr, 0, sizeof(stServerAddr));
stServerAddr.sin_family = AF_INET;
stServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
stServerAddr.sin_port = htons(port);
if(stServerAddr.sin_port == -1)
{
errHandle("port가 없습니다.");
}
/* bind */
if (bind(nServerSock, (struct sockaddr*) &stServerAddr, sizeof(stServerAddr)) == -1)
{
printf("error = %s\n", strerror(errno));
errHandle("bind() error");
}
else
{
printf("bind() - socket에 주소와 IP 할당되었습니다.\n");
}
/* 소켓을 서버용으로 사용할 수가 있게 한다. */
if (listen(nServerSock, 5) == -1)
{
printf("error = %s\n", strerror(errno));
errHandle((char*) "listen() error");
}
else
{
printf("listen() 연결 요청 대기하는 중입니다.\n");
}
return nServerSock;
}
int clientSocket(int port, char *addr)
{
int nClientSock;
struct sockaddr_in stServerAddr; /* 접속할 서버의 주소 */
nClientSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (nClientSock == -1)
{
printf("error = %s\n", strerror(errno));
errHandle("socket() error");
}
/* 서버 쪽 주소 구조체 0 초기화 */
memset(&stServerAddr, 0, sizeof(stServerAddr));
stServerAddr.sin_family = AF_INET; /* 인터넷 주소체계 사용 */
stServerAddr.sin_addr.s_addr = inet_addr(addr); /* 서버 IP 구조체에 저장 */
stServerAddr.sin_port = htons(port); /* 서버 Port 구조체에 저장 */
/* connect */
if (connect(nClientSock, (struct sockaddr*) &stServerAddr, sizeof(stServerAddr)) == -1)
{
printf("error = %s\n", strerror(errno));
errHandle("connect() error");
}
else
{
printf("연결되었습니다.\n");
}
return nClientSock;
}
/* error 처리 handler */
void errHandle(char *pMsg)
{
fputs(pMsg, stderr);
fputc('\n', stderr);
exit(1);
}
void signalHandler(int iSignals)
{
char chMessage[SIZEBUFFER];
if (iSignals == SIGINT)
{
fputs("종료하시겠습니까?(y 입력) : ", stdout);
fgets(chMessage, SIZEBUFFER, stdin);
if (!strcasecmp(chMessage, "y\n"))
{
exit(0);
}
}
}
libDefine.h
#ifndef LIBDEFINE_H_
#define LIBDEFINE_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SIZEBUFFER 1024
// 서버 socket
int serversSocket(int port);
// 클라이언트
int clientSocket(int port, char *addr);
// 에러 예외 처리
void errHandle(char* msg);
// 시그널 처리
void signalHandler(int signals);
#endif
...
TCP/IP는 원래 n바이트를 한번에 보낸다고 받는 쪽에서 n바이트를 받는다는 보장이 없습니다. 소켓통신에 대해 설명하는 아무 책에서나 다 나올 텐데요.
write를 하는 쪽에서도 리턴값을 체크해서 실제로 몇 바이트를 보냈는지 검사한 다음 아직 다 못 보냈으면 다시 write를 불러야 하고, read하는 쪽에서도 자기가 몇 바이트를 읽어야 하는지 계산한 다음 계속 리턴값을 체크하면서 그만큼 읽어야 합니다.
답변 감사드립니다.
답변 감사드립니다.
구체적으로 저기서 어떻게 추가하는 것이 좋을까요?
버퍼는 1024인데 1025자를 보낼 때 데이터가 잘려서 나올 때가 있습니다.
그리고 빈 값 넣을 때 garbage 값을 해결하는 것은 어떻게 추가하면 될까요?
도와주시면 감사하겠습니다.
어떻게 추가하면 될까요?
뛰어난 학생이라면, read와 write 함수의
뛰어난 학생이라면, read와 write 함수의 명세를 읽고, 필요한 기능을 구현하기 위하여 이들 함수를 어떻게 호출해야 하는지 스스로 구상해 볼 수 있을 겁니다.
하지만 설령 그렇게 하지 못하는 경우에도, 좋은 텍스트북이 있지요.
CSAPP 3e(https://csapp.cs.cmu.edu/) 책을 사서 처음부터 찬찬히 공부해 보시기 바랍니다.
"10.5 Robust Reading and Writing with the Rio Package"쯤까지 공부하시면 원하시는 내용이 있을 겁니다.
네 감사합니다.
감사합니다.
제 코드에서 수정할 것이 있을까요?
최대한 간결하고 명확하게 답을 드리면
네
감사합니다
감사합니다.
근데 어떤 부분을 수정하면 될까요?
만약 1025를 보낸다면
만약 1025를 보낸다면
1024로 한번 보내고
나머지 1을 보내서 총 2번을 write 한다
감사합니다.
감사합니다.
근데 1025를 보내면 1024를 받고 그 다음 패킷에서 1을 받습니다.
여기서 1024만 받고 나머지 데이터가 잘려서 나오는 것을 1을 안 받고 하는 방법이나 그런 것이 있을까요?
좀더 좋게 하자고 하면, 본인만의 프로토콜을 만드는
좀더 좋게 하자고 하면, 본인만의 프로토콜을 만드는 방법입니다.
예들면,
헤더는 정적사이즈로 64byte로 정의 하세요
64byte의 헤더 내용에는 다음 보낼 데이터의 정보를 넣어 보내줍니다.
(데이터 사이즈 /세그먼트 /체크섬 등등 기타 필요한 정보.)
헤더는 64byte 고정이기에 handshake 이후 클라이언트가 먼저 헤더를 보내던지 아니면 서버쪽에서 먼저 헤더르 보내던지 정의 한후에,
64byte만 보내고 받고 한후 이후 데이터는 헤더 내용이 있는 사이즈만큼 보내고 받으면 됩니다.
이건 딱히 좋은 방법이 아닌데요
일반적으로 1024 바이트를 한번에 write했을 때 1024 바이트가 한번에 간다는 보장이 없습니다.
마르고 닳도록 말하지만 loop를 돌려서 몇 바이트 보냈는지 세어서 다 보낼 때까지 남은 바이트를 write해야 합니다.
감사합니다.
그럼 남은 바이트는 1024만큼 받고 다음 패킷에서 받으면 되지 않나요?
...
묘하게 인터넷에서 제대로 된 예제를 찾기가 힘드네요. 요즘엔 아무도 소켓 안쓰나... -_-
아무튼 아래의 예제는 소켓프로그래밍의 경전이라 할 수 있는 Stevens의 UNIX Network Programming에서 베낀 겁니다. 이런 식으로 하시면 됩니다.
해당 파일은 http://www.kohala.com/start/unpv12e.html 여기서 "source code"를 클릭하면 다운받을 수 있습니다.
그리고 마르고 닳도록 말하지만 정말로 "이 코드를 수행하면 무슨 무슨 패킷이 왔다갔다 하는지 리눅스 커널의 TCP/IP 구현방법을 알고 싶다!" 같은 게 아니라면 "이번 패킷, 다음 패킷" 같은 개념은 그냥 잊어버리세요. TCP는 스트림 프로토콜입니다. 내부적으로 패킷이 몇 개 왔다갔다하는지는 사용자 입장에서 전혀 모르는 일이고, 그런 걸 가정하고 코드를 짰으면 십중팔구 틀린 코드라고 생각하면 됩니다.
감사합니다
감사합니다.
written 함수에서 어떤 것을 하고 있나요?
댓글 달기