2009-11-14 4 views
2

클라이언트에서 전체 구조체를 보내고 서버에서 구조체 멤버 "ID"를 출력하려는 ​​클라이언트 및 서버 프로그램이 있습니다.TCP를 통해 struct 보내기 (C 프로그래밍)

내가 모든 연결 등을 수행하고 이미 통해 문자열 보내 처리했다 : 그래서

send(socket, string, string_size, 0); 

를, 그것은 전송을 통해 문자열 대신 구조체를 보낼 수()? 서버의 버퍼를 같은 유형의 빈 구조체로 바꾸고 이동시킬 수 있습니까?

+0

유익한 답변을 보내 주신 모든 분들께 감사드립니다. – o01

답변

1

Welll ... 네트워크를 통해 구조체를 보내는 것은 제대로 수행하면 다소 어렵습니다.

send(socket, (char*)my_struct, sizeof(my_struct), 0); 

그러나 여기 일이 :

  • 를 sizeof (my_struct) 클라이언트와 서버 사이에 변경 될 수 있습니다 당신이 말하는하여 네트워크를 통해 구조체를 보낼 수 있습니다 -

    칼 권리입니다. 컴파일러는 약간의 패딩과 정렬을 수행하기 때문에, #pragma pack()을 사용하여 정렬을 명시 적으로 정의하지 않으면 그 크기가 다를 수 있습니다.

  • 다른 문제는 바이트 순서 인 경향이 있습니다. 일부 기계는 빅 엔디안이고 다른 기계는 리틀 엔디안이므로 바이트의 배열이 다를 수 있습니다. 실제로 서버 또는 클라이언트가 비 Intel 하드웨어에서 실행되지 않는 한 (아마도 그렇지 않은 경우) 이론적으로이 문제는 실제보다 많이 존재합니다.

사람들이 자주 제안하는 솔루션은 구조체를 serialize하는 루틴을 갖는 것입니다. 즉, 한 번에 하나의 데이터 멤버를 구조체로 보내 클라이언트와 서버가 프로그램에 코딩 한 정확한 바이트 수를 send() 및 recv()로만 전달하도록합니다.

+0

별로 중요하지 않지만 구조체를 char *가 아닌 void *로 캐스팅해야합니다. 왜냐하면 send()가 프로토 타입을 작성하기 때문입니다. –

+0

@rasher : 구조체를 패키징하고 직렬화하는 것에 대해 언급하는 동안 많은 양의 데이터를 전송하는 경우 직렬화가 성능을 죽일 수 있다는 점을 지적하는 것이 중요합니다. 전송을 요청할 때마다 사용자 공간과 커널 공간간에 상당히 비싼 컨텍스트 전환이 발생합니다. 데이터를 포장하는 것이 바람직한 방법입니다. –

0

그래, 같은 유형의 구조체는 모두 같은 크기입니다. 포인터를 올바르게 캐스팅하면 잘 할 수 있습니다.

+0

Goodie! 이 접근 방법에 대한 작고 간단한 예를 제공해 주시겠습니까? – o01

+0

정말 그렇게 생각하지 마십시오 ... 구조체 멤버에 적용된 패딩에 따라 다르므로 같은 형식의 구조체는 같은 크기가 아닙니다. 또한 아키텍처 (32/64) 및 엔디안 문제도 있습니다. –

+0

Stefano와 다른 것은 맞습니다. 이것은 호환 가능한 C 아키텍처로 작업하는 경우에만 작동합니다. 당신은 이것이 사실이라고 확인 했으므로 다음과 같이 send를 할 수 있습니다. (untested, sorry!) send (socket, (char *) & yourStruct, sizeof (yourStruct), 0). 솔루션을하지만 구조체의 주소를 걸릴 조금 더 정확하다고 생각합니다. ('&'). –

4

클라이언트와 서버 컴퓨터가 "동일"합니까? 당신이 제안하고있는 것은 각 끝에있는 C 컴파일러가 정확히 같은 구조의 구조를 배치하는 경우에만 작동합니다. 이것이 사실이 아닐 수도있는 많은 이유가 있습니다. 예를 들어, 클라이언트와 서버 macine은 아키텍처가 다를 수 있으며, 메모리에서 숫자를 나타내는 방식 (big-endian, little-endian)은 다를 수 있습니다. 클라이언트 머신과 서버 머신이 같은 아키텍쳐를 가지고 있다고하더라도 두 개의 서로 다른 C 컴파일러는 구조체를 메모리에 배치하는 방법에 대해 다른 정책을 가질 수 있습니다 (예 : 단어 경계에 정수를 정렬하는 필드 사이의 패딩). 심지어 같은 다른 깃발을 가진 conmpiler는 다른 결과를 줄지도 모릅니다.

실용적으로 나는 클라이언트와 서버가 같은 종류의 기계라고 추측하고 있습니다. 따라서 제안하는 것이 효과적 일지 모르지만, 일반적으로 그것이 아니라는 것을 알고 있어야합니다. CORBA가 발명되었거나 사람들이 XML과 같은 일반적인 표현을 사용하는 이유.

+0

예, 클라이언트와 서버 시스템이 동일하므로 문제가되지 않습니다. 팁 주셔서 고마워요 :) – o01

2

클라이언트와 서버가 구조체를 정확히 같은 방법으로 배치 한 경우 필드가 모두 동일한 크기이고 동일한 패딩을 사용할 수 있습니다. 예를 들어 구조체에 long이있는 경우 한 컴퓨터에서는 32 비트이고 다른 컴퓨터에서는 64 비트 일 수 있습니다.이 경우 구조체가 제대로 수신되지 않습니다.

클라이언트와 서버가 항상 매우 유사한 C 구현에있을 경우 (예 : 일부 기본 개념을 배우는 데 사용하는 코드 일 수도 있고 다른 이유로 알고있는 경우) 귀하의 코드는 OSX의 현재 버전에서만 실행되어야합니다), 그러면 아마 그걸로 도망 갈 수있을 것입니다. 코드가 다른 플랫폼에서 제대로 작동하지 않을 것이고 실제 상황에서 사용하기 전에해야 할 일이 많다는 것을 기억하십시오.

대부분의 클라이언트 - 서버 응용 프로그램의 경우 일반적으로 대답 할 수 없다는 것을 의미합니다. 실제로 수행하는 작업은 전송 된 바이트 수, 순서, 의미 등을 기준으로 메시지를 정의하는 것입니다. 그런 다음 각 끝에서 사용중인 구조체가 꼭 필요한 레이아웃을 갖출 수 있도록 플랫폼에 특정한 작업을 수행합니다. 그렇다고하더라도 리틀 엔디안 구조체의 정수 멤버를 보내는 경우 바이트 스와핑을 수행해야하며 빅 엔디 언 (big-endian) 머신에서 코드를 실행해야합니다. XML, json 및 Google의 프로토콜 버퍼와 같은 데이터 교환 형식이 존재하기 때문에 이러한 까다로운 작업을 수행 할 필요가 없습니다.

[편집 : 물론 일부 구조체 멤버는 절대로 전선을 통해 보낼 수 없다는 것을 기억하십시오. 예를 들어 구조체에 포인터가 있으면 포인터가 송신기의 메모리를 참조하며 수신 측에서 쓸모가 없습니다. 이 사실이 이미 자명 한 경우 사과드립니다.하지만 C로 시작하면 누구에게나 분명하지 않습니다.

+0

기술적으로 구조체는 올바르게 연결을 통해 자신의 프로토콜이 올바르게 작동한다고 가정하면 올바르게 수신됩니다. 문제는 수신이 아니라 통역 또는 액세스라고 할 수 있습니다. – jmucchiello

+0

참. 나는 sizeof (thestruct)가 writer보다 reader에서 더 작기 때문에 당신이 실제로 모든 바이트를 application space로 읽지 않는다면, 메시지를받지 못했다고 말할 것이다. 또한 기술적으로 구조체를 통해 임의의 바이트를 복사하는 것은 정의되지 않은 동작이라고 생각합니다. 멤버의 트랩 비트를 트리거 할 수 있습니다. 따라서이 경우에도 메시지는 수신되지 않습니다. 하지만 당신 말이 맞아요, 당신은 (일부) 메시지의 바이트를 받게됩니다, 그냥 메시지 자체를 이해하지. –

1

하지만 두 가지 중요한 사항을 알아야합니다.

  1. 양 끝의 프로그램은 ABI이어야합니다. 일반적으로 양 끝이 같은 프로세서 아키텍처, 동일한 OS에서 실행되고 동일한 컴파일러 및 컴파일러 플래그로 컴파일되는 경우입니다.
  2. TCP는 스트림입니다. 전체 구조체를 전송하는지 확인해야합니다. send() 호출에 대한 문서를 살펴보십시오. 보낸 바이트의 nr을 반환합니다. 수신 측에서도 마찬가지입니다. 당신이 1 send 호출로 구조체를 전송했기 때문에, 당신이 1 recv 호출로 그것을받을 것이라는 것을 의미하지는 않습니다. 그것은 모든 조각을 얻기 위해 여러 recv 전화를 걸립니다.
0

클라이언트와 서버가 같은 방식으로 구조를 메모리에 배치하는 경우에도 일반적으로이를 수행하는 것은 좋지 않습니다.

포인터를 포함하는 복잡한 데이터 구조를 앞뒤로 전달하지 않으려는 경우에도 네트워크를 통해 데이터를 보내기 전에 직렬화하는 것이 좋습니다.

+0

데이터를 직렬화하는 것은 사용자와 커널 공간 사이에 값 비싼 불필요한 컨텍스트 전환을 유발하여 데이터를 커널 버퍼에 복사함으로써 시스템 성능을 저하시키는 최악의 솔루션입니다. 패딩이 없거나 pack pragma를 사용할 수 있도록 'struct'에 멤버를 정렬하십시오. –

+0

Robert S. Barnes : 나는 그런 무의미한 지나친 일반화에 대한 대답을 시작하는 법조차 모른다. 원래 질문에 대한 관련성을 잠시 무시하고, 측정 가능한 차이를 만들기 위해 사용자 공간과 커널 사이의 사본에 대해 얼마나 많은 양의 데이터가 있어야하는지 생각해 봅시다. 다른 한편으로, 그것에 대해 전혀 생각하지 않겠습니다. – hillu

+0

어쩌면 다시 말해야 할 것입니다. send를 호출 할 때마다 매우 비싼 컨텍스트 스위치가 발생하므로 마지막으로 일렬 처리를 수행해야합니다. 예를 들어, http://www.linfo.org/context_switch.html "컨텍스트 스위칭은 일반적으로 계산 집약적입니다. 즉 수십 또는 수백 개의 스위치 각각에 대해 나노초 단위의 상당한 프로세서 시간이 필요합니다 따라서 컨텍스트 전환은 CPU 시간면에서 시스템에 상당한 비용을 초래하며 사실 운영 체제에서 가장 비용이 많이 드는 작업이 될 수 있습니다. " "컨텍스트 전환 비용"을 찾으십시오. –

-3

데이터를 텍스트 파일로 보낸 다음 데이터를받은 후 디코딩하십시오. 데이터를 전송하려는 경우이 방법이 가장 좋습니다!

관련 문제