소켓프로그래밍 관련 질문이 있습니다.
소켓프로그래밍 질문이 있습니다.
상황1.서버(192.168.0.11)과 PC(192.168.0.10)을 소켓프로그래밍(C언어를 사용)해서 통신할 것
상황2.서버에 8바이트를 전송하면 162바이트를 받게될 것
상황3.핑은 잘나갑니다. 서버와 PC를 랜선으로 직접연결했습니다.
질문1.서버와의 통신에서 8바이트 0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E를 보내는 상황에서
1)문자열로도 전송이 되나요? ex)char packet[8]={0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E};
2)문자열이 아닌 정수형으로 전송해야하나요?
*담당 연구원님 말씀으로는 for문으로 하나씩 보내는것이 아니라 전체를 한번에 보내는 것이라고 했습니다.
질문2.8바이트를 전송한다고 했을떄
char packet[8]={0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E};로 선언해서 보내는 것이 맞나요?
질문3.아래 채팅소스를 "수정없이" 사용할 경우
connect() 오류가 뜹니다.원인이 무엇인가요?
질문4.아래는 서버와 클라이언트간의 채팅 소스중 일부분입니다.
루프백으로 검사할떄 해당 소스는 이상없이 돌아갔기에 채팅 소스를 기본틀로 잡아서 패킷전송으로 사용할려고 합니다.
클라이언트(PC)->서버
1) 8바이트 0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E 패킷을 보낸다.
2) 서버에서 recv를 한다
서버->클라이언트(PC)
1)서버에서 send를 한다
2)클라이언트에서 recv를 한다.
제가 생각한 것은
fgets은 입력받는 것이므로 제거하고.
send로 readBuffer를 보내므로 readBuffer[BUFSIZE]={0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E}로 초기화를 하고
strlen대신 readBuffer의 사이즈 sizeof(readBuffer)를 사용하는 것입니다.
이후 서버에서 recv를 하여서 8바이트를 읽어내고
서버에서 send를 합니다
마지막으로 클라이언트에서 recv를 하여 162바이트를 획득하는 방법인데 이 과정에서 오류가 있는건가요?
<서버> while (1) { retval = recv(clientsock, buf, BUFSIZE, 0); if (retval == SOCKET_ERROR) { perror("recv() 오류"); printf("recv() Error"); break; } else if (retval == 0) break;
buf[retval] = '\0';
printf("[TCP %s:%d] %s\n", inet_ntoa(clientinfo.sin_addr), ntohs(clientinfo.sin_port), buf);
retval = send(clientsock, buf, retval, 0);
if (retval == SOCKET_ERROR)
{
perror("send() 오류");
printf("send() error");
break;
}
}
<클라> while (1) { ZeroMemory(readBuffer, sizeof(readBuffer)); printf("[전송 데이터]\n");
if (fgets(readBuffer, BUFSIZE + 1, stdin) == NULL)
break;
length = strlen(readBuffer);
if (readBuffer[length - 1] == '\n')
readBuffer[length - 1] = '\0';
if (strlen(readBuffer) == 0)
break;
//recval = send(clientsock, readBuffer, sizeof(readBuffer), 0);
recval = send(clientsock, readBuffer, strlen(readBuffer), 0);
if (recval == SOCKET_ERROR)
{
printf("send()오류");
perror("send()오류:");
break;
}
printf("[TCP클라이언트] %d 바이트를 보냈습니다.\n", recval);
recval = recv(clientsock, readBuffer, recval, 0);
if (recval == 0)
{
printf("recv()오류");
perror("recv()오류:");
break;
}
readBuffer[BUFSIZE] = '\0';
printf("[TCP 클라이언트] %d 바이트를 받았습니다.\n", recval);
printf("[받은 데이터]%s\n", readBuffer);
}
첨부 | 파일 크기 |
---|---|
루프백_채팅시스템.png | 210.69 KB |
FYI
https://kldp.org/node/160132
https://kldp.org/node/160136
간단한 관련 교육 과정이라도 수료하셨다면 별 문제가
간단한 관련 교육 과정이라도 수료하셨다면 별 문제가 아닐 것인데요.
1) fgets()로 처리할 필요가 전혀 없습니다. 그래서도 안되고요.
char packet[8]={0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E};
send(소켓, packet, ...) 식으로 보내시면 됩니다.
2) 자신이 보낸 16 byte를 그대로 받은 것 같은데 의도하신 대로 된 건가요?
답변감사합니다
답변감사합니다
참고해보세요.
상황1.
서버(192.168.0.11)
PC(192.168.0.10)
소켓 통신 프로그래밍(C언어를 사용)
상황2.서버에 8바이트를 전송하면 162바이트를 받게될 것
192.168.0.11 <--- connect()
192.168.0.11 <--- send() 8 바이트
192.168.0.11 ---> recv() 162 바이트
상황3.핑은 잘나갑니다. 서버와 PC를 랜선으로 직접연결했습니다.
직접 연결하여 통신하려면. 크로스 케이블 '을 사용해야 함.
https://fillin.tistory.com/95
그렇지않다면. 공유기를 사용하세요.
질문1.서버와의 통신에서 8바이트 0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E를 보내는 상황에서
1)문자열로도 전송이 되나요? ex)char packet[8]={0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E};
//
1바이트씩 바이트 단위로 전송이 됩니다.
//
마지막 데이터가 NULL 혹은 0x00 이면. 문자열로 인식합니다.
t 는 문자
e 는 문자
s 는 문자
t 는 문자
문자열 '이라는것은 마지막이 NULL 인것을 이야기 합니다.
test
packet[0] = 't';
packet[1] = 'e';
packet[2] = 's';
packet[3] = 't';
packet[4] = 0x00; //NULL;
http://codepad.org/mN06yuea
%d 정수형 표기 116
%x 16진수 표기 74
%c 문자형 표기 t
2)문자열이 아닌 정수형으로 전송해야하나요?
//
1바이트 == 0 ~ 255의 숫자값을 입력할 수 있습니다.
(signed) char 1 byte -128 ~ 127
unsigned char 1 byte 0 ~ 255
[C++ 정리] 자료형의 크기 및 범위
http://myblog.opendocs.co.kr/archives/1230
*담당 연구원님 말씀으로는 for문으로 하나씩 보내는것이 아니라 전체를 한번에 보내는 것이라고 했습니다.
질문2.8바이트를 전송한다고 했을떄
char packet[8]={0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E};로 선언해서 보내는 것이 맞나요?
데이터가 맞다면. 맞을 수 있습니다.
서버와 클라이언트의 리틀인디언. 빅인디언 메모리 사용방식에 따라서. 다를 수 도 있습니다.
리틀/빅 인디안 변환 정리
http://kyulak.tistory.com/entry/%EB%A6%AC%ED%8B%80%EB%B9%85-%EC%9D%B8%EB%94%94%EC%95%88-%EB%B3%80%ED%99%98-%EC%A0%95%EB%A6%AC
질문3.아래 채팅소스를 "수정없이" 사용할 경우
connect() 오류가 뜹니다.원인이 무엇인가요?
접속 잘 되는 예제를 사용해서. 확인해 보시기 바랍니다.
질문4.아래는 서버와 클라이언트간의 채팅 소스중 일부분입니다.
send()
https://docs.microsoft.com/en-us/windows/desktop/api/winsock2/nf-winsock2-send
int WSAAPI send(
SOCKET s,
const char *buf,
int len,
int flags
);
0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,여기까지 문자열로 인식하더라도. recv() 에서는 0x0E 를 읽으니 괜찮음.
0xA0,0x03,0xA0,0x03,0x01,0x01,0x00,0x0E
//1바이트씩 전송 (총 8 바이트)
send(소켓, 0xA0, 1, 0);
send(소켓, 0x03, 1, 0);
send(소켓, 0xA0, 1, 0);
send(소켓, 0x03, 1, 0);
send(소켓, 0x01, 1, 0);
send(소켓, 0x01, 1, 0);
send(소켓, 0x00, 1, 0);
send(소켓, 0x0E, 1, 0);
//1바이트씩 받아서 출력 (총 162 바이트)
recv(소켓, buf, 1, 0); printf("%x %d\", buf[0], buf[0]);
recv(소켓, buf, 1, 0); printf("%x %d\", buf[1], buf[1]);
recv(소켓, buf, 1, 0); printf("%x %d\", buf[2], buf[2]);
recv(소켓, buf, 1, 0); printf("%x %d\", buf[3], buf[3]);
recv(소켓, buf, 1, 0); printf("%x %d\", buf[4], buf[4]);
recv(소켓, buf, 1, 0); printf("%x %d\", buf[5], buf[5]);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
recv(소켓, buf, 1, 0); printf("%x %d\", buf[162], buf[162]);
//%c 문자로 출력해도 되지만. 출력 범위를 넘어서 오류 발생 될 수 있음.
//-----------
fgets는 문자열을 읽어 들이니. 마지막 데이터가 0x00 NULL 인 경우 까지만 데이터를 읽습니다.
뒤에 데이터는 손실이 됩니다.
//문자열 1바이트
fgets()
strlen()
//메모리 1바이트
sizeof()
recv()
send()
read()
write()
memcpy()
fgets, fgetws
https://msdn.microsoft.com/ko-kr/library/c37dh6kf.aspx
https://www.goorm.io/
char *fgets(
char *str,
int n,
FILE *stream
);
wchar_t *fgetws(
wchar_t *str,
int n,
FILE *stream
);
스트림에서 문자열을 가져옵니다.
구문
char *fgets(
char *str,
int n,
FILE *stream
);
wchar_t *fgetws(
wchar_t *str,
int n,
FILE *stream
);
매개 변수
str
데이터의 저장소 위치입니다.
n
읽을 최대 문자 수입니다.
stream
FILE 구조체에 대한 포인터입니다.
반환 값
이러한 각 함수는 str를 반환합니다. NULL은 오류 또는 파일 끝 조건을 나타내기 위해 반환됩니다.
오류가 발생했는지 여부를 확인하려면 feof 또는 ferror를 사용합니다.
경우 str 또는 stream 가 null 포인터 또는 n 값 보다 작거나&0; 이면
이 함수는 잘못 된 매개 변수 처리기를 호출에 설명 된 대로 매개 변수 유효성 검사합니다.
계속해서 실행하도록 허용된 경우 errno 는 EINVAL 로 설정되고 함수는 NULL을 반환합니다.
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
답변감사합니다
답변감사합니다
+추가질문 있습니다.
(위에 언급한 문제는 해결되었습니다.)
위에서 클라이언트가 받은 162바이트의 데이터를 파싱을 통해서 의미있는 대상(?)으로 만들려고 합니다.
1.char buffs[recval]; //162바이트의 배열 선언,배열[0]~[161]
2.구조체 정의
2-1)typedef struct _Packet{
char head[4];
char func[4];
...
} Packet;
2-2)
typedef struct
{
typedef struct
{
unsigned short *id;
unsigned short *size;
} HEADER;
HEADER header;
char *szBuf[1024];
char *data_field;
}PACKET;
2-1)과 2-2)중 어느것이 옳은 표현방식인가요?
3.메인문에서 버퍼뒤집기:에디안때문에 뒤집는 걸로 알고 있습니다.
Packet vbyte;
vbyte.head[0]=buffs[3];
...
여기서 buffs는 char buffs[recval]로 162바이트가 선언된 것을 사용했지만
각각에 배열에 무슨 값 ex)0x7E등 은 안들어간 상태일텐데(선언은 했지만 초기화는 안한 상태)
buffs[0]=0x7E;
buffs[1]=0x10; 과 같이 162개의 배열을 직접 초기화 해주어야 하나요?
printf로 readbuffer를 읽었을떄 값이 나오긴하는데 이것을 buffs에 넣는것이 맞나요?
그러기에는 162바이트로 쪼개기 힘들거같고 헥사를 10진수로 바꾸고 바이트단위로 배열에 넣는건가요?
인디언 방식에 맞춘 구조체를 선택하면 될겁니다.
//
빅 인디언 구조체
리틀 인디언 구조체
2개 만들어서. 하나만 사용하시면 될겁니다. ㅇ_ㅇ;;
구조체를 바꾸면 되니. 메모리 복사는 그대로 하시면 됩니다.
복사 하지 않고. 구조체 버퍼를 그대로 얻어서 사용하셔도 될겁니다.
혹시 안되더라도. 구조체에 빅 인디언과 리틀 인디언이라는 표시를 해주면. 값을 확인하기 수월해 집니다.
//
전송 값은. 1바이트씩 전송해서. 확인하는 방법이 있습니다.
1 바이트는 바이트 순서가 뒤바뀌지 않습니다.
1 바이트 == 8 비트 == 0x00 ~ 0xFF == 0 ~ 255 == -128 ~ 127 == 'A' ~ 'Z' == 'a' ~ 'z'
//
리틀/빅 인디안 변환 정리
http://kyulak.tistory.com/entry/%EB%A6%AC%ED%8B%80%EB%B9%85-%EC%9D%B8%EB%94%94%EC%95%88-%EB%B3%80%ED%99%98-%EC%A0%95%EB%A6%AC
네트워크 바이트 순서 변환 함수를 사용하셔도 됩니다.
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);
//
http://codepad.org/O8diJT5f
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
우선 2-2는 안됩니다. 구조체 내부가 포인터만
우선 2-2는 안됩니다. 구조체 내부가 포인터만 있으니까요.
2-1도 안됩니다. byte alignment와 padding 이슈 때문에 권장하지 않습니다.
그나마 char buffs[recval]; 가 제일 좋습니다.
162 바이트에 대한 구조가 나와 있으면 그걸 가지고 char, short int, int 등으로 파싱해서 쓰면 됩니다. 가령 int (4 bytes)는 char 4개를 모으면 되겠죠. 필요에 따라 endianess를 바꿔주어야 할 수도 있으실 겁니다.
답변감사합니다. +추가질문이 있습니다.
답변 감사합니다. 이제 위의 모든 문제는 해결되었습니다. 근데 추가질문이 있습니다.
라즈베리파이(서버)와 PC간의 통신에서 8바이트를 보내고 162바이트를 받은후 파싱을 하고 타이머까지 이상없이 코드를 설계했습니다.
질문1)라즈베리파이와 PC간의 1:1랜선 소켓통신에서는 162바이트를 정확하게 받아냅니다. 이제 이것을 공작기계-라즈베리파이-PC로 공유기를 이용해서 설치한 PC에서 파일을 실행시켰는데 이상하게 149바이트를 받아냅니다.(해당 파일은 라즈베리파이와 PC간의 1:1랜선 소켓통신에서 162바이트를 제대로 전송합니다.)
버퍼[0]~버퍼[161]까지 모두 출력해서 서로 비교를 하여 어느부분이 빠졌는데 확인해야하나요? 162바이트에서 비는 부분(162-149) 만큼 정보가 누락됩니다. 코딩할떄 버퍼사이즈는 BUFSIZE 2048으로 설정해서 넉넉합니다.
질문2.파싱을 통해 바이트 단위로 읽어낸 것을 재배열한 후 좌표값으로 환산을 하여 공작기계의 장비상태(xyz좌표 및 공구의 이동속도등)을 알아는 코딩입니다.
예컨데 vbyte.rPos_x [0] [1] [2] [3] 은 buffs[3] buffs[2] buffs[1] buffs[0]의 값을 각각 가지며 이경우 rPos_x는 절대좌표x를 지칭합니다.
buffs[3]=FFFFFFFF
buffs[2]=FFFFFFFF
buffs[1]=FFFFFFFE
buffs[0]=FFFFFFD4
이런식으로 버퍼를 받았으면 이것을 십진수로 바꾸어서 x좌표를 얻어낼 수 있다고 합니다.
이때 각각을 십진수로 바꾸었더니 buffs[3]=-1 ,buffs[2]= -1, buffs[1]=-2, buffs[0]=-44 의 값을 가집니다. 이떄 rPos_x의 값은 (-1) + (-1) + (-2) + (-44)로 계산되어 -48이라고 생각됬는데 잘못계산한 건가요?
혹시 바이트단위로 받은후 (rPos_x는 4바이트의 크기를 가지는 배열)
4바이트전체 FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFD4를 전체적으로 보수를 고려하여 계산해야하나요?(32비트) 아니면 바이트단위로 십진수로 변환하되 합산하는 과정에서 문제가 있는건가요?
사진3장 첨삭합니다.
참고해보세요.
//
성공한 데이터를 1바이트씩 출력
실패한 데이터를 1바이트씩 출력
값이 다른 부분을. 비교해서 확인해보세요.
//
바이트 수 가 다른것은 개발사에 문의해보세요.
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
잘 정리하셨네요.
잘 정리하셨네요.
질문 1) 실제 네트워크에서 문제가 생겨 적게 받을수는 없습니다. 분명히 보내는 쪽에서 149 만큼만 보내거나 또는 잘못 읽어오는 것입니다. wireshark로 보면 알수 있습니다. 보내는 쪽이 162 바이트를 보내는지 아니면 149 바이트를 보내는지.
질문 2) buffs는 우선 char 형이라고 가정하겠습니다. 이럴경우 "%02x", (char)buffs[0] 등으로 프린트해야 정확한 값을 알수 있을 것입니다. 예를 들어 위의 경우 FFFFFED4가 되어 int32 형 (4 바이트, big endian)으로는 -300 으로 보여지네요. 같은 endian일 경우 rPos_x = *(int *)&buffs[0] 이런 식으로 읽을 수 있는데 endian 이 다르면 swap 해주어야 합니다.
답변감사합니다. +추가질문있습니다.
02x를 고려해서 해보니까 제가받은 buffs의 각 뒷부분 2개씩을 짜른후(1바이트) 4바이트로 합치니까 FFFFFE40 이라는 값을 얻고 이값이 제가 원하던 값과 동일했습니다. 나머지 부분도 같은 방법을 사용하니 원하는 값과 일치합니다.
질문1.저는 char buffs를 지정했습니다. 또한 이것을 %x로 출력했더니 FFFF FFFF이 나왔는데 16진수라면 FF(11111111)가 나온 시점에서 이미 1바이트(char의 크기)만큼 나와야하는거 아닌가요?
인터넷에 찾아보니 연산시 자동승격? 과 관련된 글도 있는데 그것떄문인가요?
질문2.buffs의 뒷부분 2개만을 따오는 방법이 무엇인가요? printf("%02X\n",buffs[3])을 통해 출력 자체는 뒷자리 2개씩이 가능합니다. 2개씩 저장한것을 buffss라고 정의할때
4바이트가 합쳐진 buffss[3] buffss[2] buffss[1] buffss[0] 으로 표현하고
마지막에 buffss를 하나로 만들어서 FFFFFED4로 만든후 %d로 변환시키려고 합니다.
참고해보세요.
int 는 4바이트 * 8비트 = 32비트
char 는 1바이트 = 8비트
//16진수 HEX 헥사 표기
0x00 == 1바이트
0xFF == 1바이트
0x0 1234 == 2바이트
0x0 12345678 == 4바이트
singned 와 unsigned 는. 데이터 값이 다릅니다.
<< 8 왼쪽 8비트 쉬프트 연산자
>> 8 오른쪽 8비트 쉬프트 연산자
http://codepad.org/MaW3193l
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
질문 1) printf의 마법입니다. 하나의 함수로
질문 1) printf의 마법입니다. 하나의 함수로 모든 경우를 처리할 수 없기 때문에 어느 정도 보편성을 가지고 지원하고 있습니다. 자신이 원하는 정확한 출력을 위해서는 printf fmt 를 정확하게 입력하여야 합니다.
질문 2) buffs가 (unsigned, signed) char 형이라면 이미 1 byte 짜리 형입니다. printf() 함수에 의해서 길게 보이도록 프린트되는 것 뿐입니다. 출력 자체에 현혹되시면 안됩니다(중요). 그냥 그대로 쓰시면 됩니다. 네개의 char를 가지고 int 형을 만드시려면
int a = (buffs[3] << 24) + (buffs[2] << 16) + (buffs[1] << 8) + buffs[0];
식으로 하시면 됩니다. endian 때문에 버퍼 인덱스는 반대로 해주어야 할 수도 있어요.
+
+답변감사합니다. 궁금한게 한개 더 있습니다.
해당 코딩에서 십진수로 변환한 값이 255까지밖에 안됩니다.
예를 들면 FF FF F8 30 은 십진수로 변환시 -2000의 값을 가집니다.
저는 위의 예처럼 음수까지 표현하고 싶은데 이럴경우는 어떻게 하나요?
"%02X", (unsigned char)buffs
"%02X", (unsigned char)buffs[0] 로 하시면 됩니다.
그리고 데이터를 받을 때에는 unsigned char buffs[] 로 선언하시는 것이 좀더 다른 부분에 덜 신경을 쓰게 되어 더 났습니다.
음... 무슨 질문인지 이해가 잘 안되네요. char
음... 무슨 질문인지 이해가 잘 안되네요. char buffs[]가 int 형의 데이터를 조합하기 위한 raw packet이라고 하면 buffs는 하나의 데이터 조각일 뿐 그 외에는 아무런 의미도 없는 것입니다. printf로 찍는 것 자체가 의미가 없습니다. 오로지 debugging 을 위해서 잠깐 볼 뿐인거죠. 나중에 잘 돌아가면 이 부분은 모두 주석하시면 됩니다.
중요한 것은 buffs 4개가 모여 int 형 하나가 되는 것입니다. int는 충분이 크기 때문에 -2000 도 문제없이 표현이 가능하겠죠.
답변감사합니다.
답변감사합니다. 문제해결되었습니다.
댓글 달기