윈도우 소켓프로그래밍 관련 질문있습니다.
몇일전에 과제때문에 질문올렸던 학생입니다.
윈도우 소켓프로그래밍 관련이 질문이 있습니다.
과제 조건
1.PC와 장비(회사내 장비)와 소켓 프로그래밍을 통해 통신을 한다.
2.PC에서 장비로 8바이트{0xA0 0x03 0xA0 0x03 0x01 0x01 0x00 0x0E}를 전송한다.
3.장비의 ip는 192.168.0.11 포트번호는 5001이다.
4.장비는 8바이트의 정보를 받아 162바이트의 통신 패킷을 전송한다.
5.PC에서 162바이트가 잘왔는지 확인한다.
-----------------------------------------------
이후 162바이트를 파싱을 한다.
현재상황
char hex[]="A003A0030101000E"를 보냈더니
0x00001100 의 데이터크기를 받아 바이트로 환산하니 4392바이트를 받았습니다.
질문사항
1.PC에서 8바이트를 보내라할때
char hex[]="A003A0030101000E"로 보냈을떄 보내는 바이트는 16바이트입니다.
또한 받게되는 바이트 역시 16바이트입니다.(제가 원하는건 8바이트를 보내고 162바이트를 받는것입니다)
1-1)문자열로 데이터를 보내는게 아니라 정수형으로 보내야하는건가요?
1-2)만약 보낸다면 길이를 고려했을떄 변수1,변수2로 나누어서 보내야하나요?
1-3)아니면 배열로 {0xA0 0x03 0xA0 0x03 0x01 0x01 0x00 0x0E}을 선언해서 send로 보내야하는건가요?
2.서버 소스에서는 INADDR를 통해 모든 아이피를 받을 수 있게 하는건 이해가 됩니다.
클라이언트 소스에서는 "127.0.0.1"을 사용하면 자신의 PC에서 즉 cmd창 2개로 띄어서 하는 통신만 가능한건가요?
3.8바이트를 보냈을때 문자열이 아니라 데이터라서 장비가 데이터를 받고 8바이트가 아닌 162바이트를 내보내는 건가요?
4.구글링을 통해서 확보하는 소스코드 중에 connect() 대상 컴퓨터에 연결을 할 수 없다는 메시지창이 나옵니다. 방화벽은 모두 끈 상태인데 소스코드의 문제인가요?
5.소켓 프로그래밍을 독학하기에 추천해주실만한 책이나 사이트 혹시 알려주실 수 있나요?
//서버 순서:원속초기화-소켓생성-(데이터통신:bind,listen,accept)-소켓해제-원속종료 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<winsock2.h> //리눅스 소켓프로그래밍과는 다르게 윈도우 소켓프로그래밍에서 이것을 선언해야됨 #pragma comment(lib,"ws2_32.lib") #define BUFSIZE 2018 void ErrorHandling(char* message); size_t len_test(char* s); size_t len_test(char* s) { return strlen(s); } int main(int argc, char *argv[]) { //원속 소켓프로그래밍에서 사용자가 만드는 구조체를 제외하고 필수적으로 만들어 놓은 구조체들 SOCKET sock, clientsock; //실제 통신을 위한 소켓 구조체 WSADATA wsa; //윈속 초기화 WSAStartup 함수 호출을 위한 구조체선언 struct sockaddr_in sockinfo, clientinfo; //소켓에 IP/PORT를 할당(bind) 시키기 위해 중간에 주소를 저장해줄 구조체 //AF_INET(인터넷 주소 체계)를 사용해서 Sockaddr_in 구조체를 사용 int clientsize; //클라이언트 소켓 변수 clientsock과 clientinfo 그리고 int clientsize char message[] = "A003A0030101000E"; printf("서버에서 보내는 메시지의 크기 %u\n", len_test(message)); long HEX1 = 0xA003A003; long HEX2 = 0x0101000E; if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) //MAKEWORD(2,2)는 매크로 함수 ErrorHandling("Winsock DLL 실패"); //윈도우 소케프로그래밍을 사용하기 위해서 Winsock.dll 파일로드 & WSACleanup 함수로 해제 //int WSAStartup(WORD ___,LPWSADATA ___); 초기화 함수 포맷 // 원속버전,WSAData의 포인터를 전달하여 시스템에 제공하는 원속 정보를 얻어옴(구조체 주소를 전달) //원속 초기화 작업 완료 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // [성공시 소켓 핸들,실패시 INVALUID_SOCKET] //소켓생성함수로써 af,type,protocol //Af 주소영역 지정:인터넷 영역,IPX영역 그외 통신영역,보통 인터넷 통신이라서 AF_INET //Type: 연결지향 TCP->SOCK_STREAM 비연결지향 UDP->SOCK_DRAME,TCP/IP소켓 프로그래밍이므로 전자를 선택 //Protocol TCP->IPPROTO_TCP UDP->IPPROTO_UDP if (sock == INVALID_SOCKET) ErrorHandling("Soecket 생성 실패"); //HANDLE(핸들) 운영체제는 자신이 관리하는 자원이나 정보를 보호하기 위해서 //자신이 관리하는 자원이나 정보의 실질적인 주소를 응용 프로그램에게 알려주지 않고 //그것을 암시하는 값만 전달하는 방식을 사용 //소켓 프로그래밍에서 성공시 0값으로 리턴하는 함수와 핸들값으로 리턴하는 함수가 있다. //반환 값을 확인할 떄는 INVALID_SOCKET을 이용 //핸들은 프로그램 내부에서 처리하는 암시적인 값,따라서 변수선언하여 저장,INVALID_SOCK 를 사용해서 확인 //소켓 생성 작업 완료 memset(&sockinfo, 0, sizeof(SOCKADDR_IN)); //★sizeof(sockinfo) ? sockinfo.sin_family = AF_INET; //주소체계 AF_INET,AF_INET6,AF_LOCAL sockinfo.sin_port = htons(5001); //★ htons(atoi("9190")); sockinfo.sin_addr.s_addr = htonl(INADDR_ANY); //ip를 지정함 //서버 입장에서는 모든 인터넷 주소로 클라이언트를 대기해야하므로 INADDR_ANY 0.0.0.0 주소를 사용 //모든 사용 가능한 주소로 부터 대기 //소켓 주소 구조체 if (bind(sock, (SOCKADDR*)&sockinfo, sizeof(sockinfo)) == SOCKET_ERROR) //[성공시 0,실패시 SOCKET_ERROR] ErrorHandling("bine 실패"); // 소켓값,(SOCKADDR*)&소켓주소 구조체 저장값,sizeof(소켓주소 구조체 저장값) if (listen(sock, 5) == SOCKET_ERROR) //[성공시 0,실패시 SOCKET_ERROR] ErrorHandling("listen 실패"); printf("클라이언트로부터 접속을 기다리고 있습니다...\n"); //대기상태를 의미 //list(SOCKET,backlog) 소켓 생서시 만들어서 저장했던 소켓 핸들값과 통로값 clientsize = sizeof(clientinfo); clientsock = accept(sock, (SOCKADDR*)&clientinfo, &clientsize); // [성공시 소켓 핸들,실패시 INVALUID_SOCKET] //서버 프로그램 안에 클라이언트 연결 소켓을 만듬 //서버소켓,(SOCKADDR*)&클라이언트주소값,&클라이언트 주소값 사이즈 if (clientsock == INVALID_SOCKET) //소켓핸들값이라서 INVALID_SOCKET로 확인 ErrorHandling("accept 실패"); send(clientsock, message, sizeof(message), 0); send(sock, &HEX1, sizeof(HEX1), 0); send(sock, &HEX2, sizeof(HEX2), 0); //send(clientsock, hex, sizeof(hex), 0); closesocket(sock); closesocket(clientsock); printf("성공\n"); WSACleanup(); } void ErrorHandling(char *message) { WSACleanup(); fputs(message, stderr); fputc('\n', stderr); exit(1); } //클라이언트 순서:초기화-소켓생성-connect-소켓해제-원속 종료 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<winsock2.h> #pragma comment(lib,"ws2_32.lib") #define BUFSIZE 2048 void ErrorHandling(char *message); int main(int argc, char *argv[]) //int argc는 메인함수에 전달되는 데이터의 갯수,argv는 문자열 //char* argv[]는 메인함수에 전달되는 실제적인 데이터로 char형 포인트 배열로 구성 { SOCKET clientsock; // 클라이언트 프로그램 소켓 생성 WSADATA wsa; struct sockaddr_in sockinfo; // 서버 연결 할 주소 구조체 생성 char message[BUFSIZE]; // recv 함수 메시지 받을 배열 선언 int strlen; // recv 리턴 값 저장 할 변수 선언 long HEX1 = 0xA003A003; long HEX2 = 0x0101000E; long strlen1; long strlen2; if (argc != 3) // 입력 ( IP , PORT ) 가 입력되지 않을 때 오류 표시,2개를 입력받았고 +1을 해서 3이 되야 정상 { printf("Usage : %s <IP> <PORT> \n", argv[0]); exit(1); } if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) ErrorHandling("Winsock DLL error"); clientsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientsock == INVALID_SOCKET) ErrorHandling("Soecket 생성 실패"); memset(&sockinfo, 0, sizeof(sockinfo)); // 주소 구조체 함수 초기화 sockinfo.sin_family = AF_INET; sockinfo.sin_port = htons(atoi(argv[2])); // 포트를 두 번째로 입력 받음 //★ htons(atoi("9190"); sockinfo.sin_addr.s_addr = inet_addr("127.0.0.1"); //inet_addr(argv[1]); // IP를 첫 번째로 입력 받음 //sdl검사해제,여기서 입력하는 ip는 서버의 ip주소 if (connect(clientsock, (SOCKADDR*)&sockinfo, sizeof(sockinfo)) == SOCKET_ERROR) //[성공시 0,실패시 SOCKET_ERROR] // 서버 측에 연결 요청 ErrorHandling("accept 실패"); // 클라이언트소켓,(SOCKADDR*)&서버주소구조체,sizeof(서버주소구조체) strlen = recv(clientsock, message, sizeof(message) - 1, 0); //메시지를 수신하여 저장하는 함수 if (strlen == -1) // recv 함수 반환 값이 -1이면 메시지 수신 실패 ErrorHandling("message 실패"); printf("받은 데이터는 16진수로 0x%08X 입니다.\n", htons(strlen)); printf("받은 데이터는 십진수로 %d 입니다.\n", htons(strlen)); printf(" Server say: %s \n", message); // 수신 된 메시지 표시 closesocket(clientsock); //[성공시 0,실패시 SOCKET_ERROR] WSACleanup(); } void ErrorHandling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); } <\code>
참고해보세요.
send(소켓, 데이터, 크기, 플래그); 함수에 보내는 크기가 있습니다.
1 바이트씩 보내서 확인해 보세요.
char a;
a = 'a';
send(소켓, a, 1, 0);
char b[2];
b[0] = 'a';
b[1] = 'b';
send(소켓, b, 2, 0);
char c[3];
c[0] = 'a';
c[1] = 'b';
c[2] = 'c';
send(소켓, c, 3, 0);
char d[4];
d[0] = 'a';
d[1] = 'b';
d[2] = 'c';
d[3] = 0x00; //문자열 사용시. 문자열의 마지막을 구분하기 위한 0x00 NULL 데이터
send(소켓, d, 3, 0);
strcpy(&d[0], "123");
char e[3];
strncpy(&e[0], "123", 3); //3 바이트만 복사
char d[3];
memcpy(&f[0], "123", 3); //3 바이트만 복사
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
답변감사합니다
답변감사합니다
클라이언트에서printf("받은 데이터는 16진수로
클라이언트에서
여기서 strlen은 recv 함수의 반환값으로, htons 함수를 적용하면 안됩니다. htons 를 빼고 그냥 출력하세요.
htons 함수의 역할을 이해하고 싶으시면 endian을 검색해보세요.
1. 이건 서버랑 클라이언트 정해야 할 문제네요. 장비에 이미 기존의 서버프로그램이 있는 건가요? 아니면 위의 서버프로그램을 장비에서 실행을 할건가요? 전자라면 기존 서버가 어떤 데이터를 받는지를 알아야 할테고, 후자라면 정수든 문자열이든 아무거나 편한대로 쓰면 됩니다.
2. 127.0.0.1은 자신의 pc를 나타내는 ip주소입니다. 다른 컴퓨터로 연결하려면 해당 컴퓨터의 ip주소를 사용해야 합니다. 현재 클라이언트는 무조건 127.0.0.1로 연결하도록 되어 있네요.
3. 질문의 의미를 모르겠네요.
4. 위의 클라이언트 코드를 말하는 건가요? 일단 connect 부분까지는 별 문제는 없어 보입니다.
올바른 ip를 사용하고 있는 건지 다시 확인해보세요. telnet 같은 걸 이용해서 서버 해당 포트에 연결이 가능한지도 테스트해보세요.
5. ms 사이트의 예제 코드를 참조하시는 것도 좋을 것 같습니다. https://docs.microsoft.com/en-us/windows/desktop/winsock/getting-started-with-winsock
답변감사합니다
1.서버가 어떤 데이터를 받는지:0xA0 0x03 0xA0 0x03 0x01 0x01 0x00 0x0E 패킷을 보내는건데 8바이트니까 A0003A0030101000E 맞는건가요?
2.소스코드에서 127.0.0.1 과 inet_addr(argv[1]); 로 비교했더니 127.0.0.1이 되는걸로 보아 제가 클라이언트 맞습니다.
3.서버로 8바이트를 보내고(=랜선으로 다이렉트로 PC와 연결된 장비에 8바이트를 보내고) 장비에서 162바이트를 받아내라는데 저도 이해가 안갑니다.
답변 감사합니다.
리눅스의 경우 nc 라는 커맨드로 쉽게 상대방 ip에
리눅스의 경우 nc 라는 커맨드로 쉽게 상대방 ip에 뭔가 보낼 수 있습니다.
nc를 이용해서 8 바이트를 장비에 보내보시고, wireshark로 네트워크 패킷을 모니터링해서 장비 쪽이 162 바이트를 실제로 보내는지 확인해 보세요.
답변감사합니다
이 경우는 장비쪽에 문제가 있는건가요?
빨간색으로 나오면 문제가 있다는걸로 들었습니다.
RST, ACK가 뜨는 것을 보니 어느 한쪽에서
RST, ACK가 뜨는 것을 보니 어느 한쪽에서 connect가 되지 않은 것으로 보이는데 장비쪽에 ping이 되나요? 장비쪽 포트가 open/listen 상태로 있는지 확인해 보세요.
참고해보세요.
src port = 55392
dst port = 8193
포트 번호가 다른거 같네요.
웹 브라우저 - 접속 확인
http://192.168.0.11:5001
텔넷 - 접속 확인
telnet 192.168.0.11 5001
Hex to ASCII text converter 헥사값을 문자로 변환하는 사이트 - 텟넷에서 붙여넣기 될 수 도 있겠네요. ㅇ_ㅇ;;
https://www.rapidtables.com/convert/number/hex-to-ascii.html
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
참고해보세요.
//랜선
- 직접 연결 : 크로스 케이블 (4가닥에 선 위치가 반대)
- 간접 연결 : 공유기 허브
//IP 확인
- ipconfig 윈도우
- ifconfig 리눅스
//네트워크 장비의 주소 정보 얻는 방법
- arp -a
//포트 상태 정보 얻는 방법
- netstat -na
//접속 완료
장비 ip 192.168.0.11
포트번호 5001
connect(소켓, 소켓구조체, 소켓구조체 크기)
//전송 완료
char data[8];
data[0] = 0xA0;
data[1] = 0x03;
data[2] = 0xA0;
data[3] = 0x03;
data[4] = 0x01;
data[5] = 0x01;
data[6] = 0x00;
data[7] = 0x0E;
send(소켓, 데이터 배열, 데이터 크기, 플래그);
//받기 완료
char data[1024];
int r;
r = recv(소켓, 데이터 배열, 데이터 크기, 플래그);
//받은 데이터 출력
r = 받은 데이터 갯수
data = 받은 데이터값
printf("받은 데이터 갯수 : %d\n", r);
printf("받은 데이터값 : %s\n", data);
int i;
for(i=0; i<1024; i++)
{
printf("받은 데이터값 1 바이트씩 출력 : %x\n", data[i]); //16진수 헥사값 출력
// printf("받은 데이터값 1 바이트씩 출력 : %c\n", data[i]); //char 형 출력
}
1 byte 바이트 단위 == 1 char == 8 bit 비트 == 0x00 ~ 0xFF == unsigned 0 ~ 255 == signed -128 ~ 127
[C++ 정리] 자료형의 크기 및 범위
http://myblog.opendocs.co.kr/archives/1230
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
답변감사합니다
답변감사합니다
+추가질문이 있습니다.
제가 사용하는 PC와 장비간의 소켓통신
1.장비의 ip는 192.168.0.11 이고 이를 http://192.168.0.11에서 다른 정보까지 알아봤습니다.
pc ip를 192.168.0.10 포트번호는 8193
장비 ip는 192.168.0.11 포트번호는 5001
192.168.0.11으로의 핑은 잘 나갑니다.
장비(192.168.0.11)에서 PC(192.168.0.10)으로의 전송은 오류가 없지만 장비쪽 포트번호가 랜덤입니다.
PC(192.168.0.10)에서 장비(192.168.0.11)으로의 전송은 오류가 생기고 장비쪽 포트번호가 랜덤입니다.
이 상황에서 장비쪽에 문제가 있는건가요?
1)제 생각에는 장비쪽 포트번호가 5001으로 사용되지 않아서 문제가 되는것 같은데
cmd창에 클라이언트 응용프로그램 넣고 192.168.0.11 5001 으로 연결되는것이 아닌가요?
2)아니면 수식은 맞지만 따로 장비 포트를 여는 작업이 필요한건가요?
(장비와의 연결문제로 162바이트 전송을 못받습니다.)
혹시 해서 코드도 같이 올립니다.
네이트온으로 원격 신청 요청하세요.
ㅇ_ㅇ;;
shintx@nate.com
- 장비 업체에도 문의해보시구요.
//텔넷으로 HEX 값 문자로 보내는 방법도 있는거 같네요.
telnet server port << EOF
\x0B\x84\x31\x32\x33\x34\x35\x36\x37\x38\x00
more commands
more commands
quit
EOT
How to send a Hex message to server (Php and CLI)
https://community.spiceworks.com/topic/721790-how-to-send-a-hex-message-to-server-php-and-cli
telnet client - output hexadecimal network traffic on debian
https://unix.stackexchange.com/questions/81937/telnet-client-output-hexadecimal-network-traffic-on-debian
telnet> set netdata
Will print hexadecimal representation of network traffic.
telnet> set termdata
Will print hexadecimal representation of terminal traffic.
Putty
https://learn.adafruit.com/windows-tools-for-the-electrical-engineer/serial-terminal
windows 10에서 telnet 사용하기
https://opentutorials.org/module/2160/12506
윈도우7(Windows7)에서 텔넷(Telnet) 설치하기
http://ooz.co.kr/75
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
답변감사합니다
답변감사합니다
+추가 질문이있습니다.
네트워크장비에서 연결포트가 5001이라서 서버&클라이언트 코딩에 5001을 넣었는데 와이어샤크 분석결과 8193(PC쪽 포트로 예상됨,네트워크장비에서 기계 ip 192.168.0.10 /포트 8193)은 고정값으로 받지만 다른 쪽은 5xxxx 와 같이 랜덤값으로 받습니다. 코딩에 문제가 있는건가요?
네이트온으로 원격 신청 요청하세요.
shintx@nate.com
----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.
매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.
각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com
댓글 달기