일반 네트워크 프로그래밍의 쓰기 버퍼 정보
-
11-09-2019 - |
문제
나는 boost.asio를 사용하여 서버를 작성하고 있습니다. 각 연결에 대한 버퍼를 읽고 쓰고 비동기식 읽기/쓰기 기능을 사용합니다 (async_write_some
/ async_read_some
).
읽기 버퍼와 함께 async_read_some
, 문제 없다. 그냥 호출 async_read_some
읽기 버퍼는 읽기 핸들러에서만 읽히기 때문에 기능은 괜찮습니다 (일반적으로 같은 스레드에서는).
그러나 여러 스레드에서 쓰기 버퍼에 액세스해야하므로 수정하려면 잠겨 있어야합니다.
첫 번째 질문!
쓰기 버퍼의 잠금을 피할 수있는 방법이 있습니까?
내 자신의 패킷을 스택 버퍼에 쓰고 쓰기 버퍼에 복사합니다. 그런 다음 전화하십시오 async_write_some
패킷을 보내는 기능. 이런 식으로, 두 개의 패킷을 연쇄로 보내면 괜찮습니까? async_write_some
두 번 기능 하시겠습니까?
두 번째 질문!
소켓 프로그래밍에서 비동기 쓰기의 일반적인 방법은 무엇입니까?
읽어 주셔서 감사합니다.
해결책
답변 #1 :
잠금이 실행 가능한 접근법이라는 것이 맞습니다. 그러나이 모든 것을 수행하는 훨씬 간단한 방법이 있습니다. 부스트 strand
. 스트랜드를 사용하여 래핑 한 콜백은 콜백을 실행하는 스레드에 관계없이 직렬화되고 보장됩니다. 기본적으로, 그것은 당신을 위해 모든 잠금을 처리합니다.
이것은 당신이 원하는만큼 많은 작가를 가질 수 있고, 그들이 모두가 싸우는 경우 같은 가닥 (따라서 모든 작가들 사이에서 단일 가닥을 공유하십시오) 그들은 일련의 실행을 수행합니다. 조심해야 할 한 가지는 모든 글을 수행하기 위해 메모리에서 동일한 실제 버퍼를 사용하려고하지 않도록하는 것입니다. 예를 들어, 이것은 피해야 할 것입니다.
char buffer_to_write[256]; // shared among threads
/* ... in thread 1 ... */
memcpy(buffer_to_write, packet_1, std::min(sizeof(packet_1), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);
/* ... in thread 2 ... */
memcpy(buffer_to_write, packet_2, std::min(sizeof(packet_2), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);
거기에서 실제 쓰기 버퍼를 공유하고 있습니다 (buffer_to_write
). 대신 이런 일을했다면 괜찮을 것입니다.
/* A utility class that you can use */
class PacketWriter
{
private:
typedef std::vector<char> buffer_type;
static void WriteIsComplete(boost::shared_ptr<buffer_type> op_buffer, const boost::system::error_code& error, std::size_t bytes_transferred)
{
// Handle your write completion here
}
public:
template<class IO>
static bool WritePacket(const std::vector<char>& packet_data, IO& asio_object)
{
boost::shared_ptr<buffer_type> op_buffer(new buffer_type(packet_data));
if (!op_buffer)
{
return (false);
}
asio_object.async_write_some(boost::asio::buffer(*op_buffer), boost::bind(&PacketWriter::WriteIsComplete, op_buffer, boost::asio::placeholder::error, boost::asio::placeholder::bytes_transferred));
}
};
/* ... in thread 1 ... */
PacketWriter::WritePacket(packet_1, my_socket);
/* ... in thread 2 ... */
PacketWriter::WritePacket(packet_2, my_socket);
여기서 스트랜드를 WritePacket에 전달하면 도움이됩니다. 그래도 아이디어를 얻습니다.
답변 #2 :
나는 당신이 이미 아주 좋은 접근 방식을 취하고 있다고 생각합니다. 내가 제공 할 제안 중 하나는 사용하는 것입니다 async_write
대신에 async_write_some
콜백이 호출되기 전에 전체 버퍼가 작성됩니다.
다른 팁
죄송하지만 두 가지 선택이 있습니다.
잠금 장치로 쓰기 문을 세리어링하거나 대기열에서 요청을 읽는 별도의 라이터 스레드를 더 잘 시작하면 다른 스레드는 너무 많은 경합없이 큐의 요청을 쌓을 수 있습니다 (일부 뮤 테이징이 필요합니다).
각 작문 스레드에게 자체 소켓을 제공하십시오! 와이어의 다른 쪽 끝에있는 프로그램이이를 지원할 수있는 경우 실제로 더 나은 솔루션입니다.
수정을 대기하고 쓰기 핸들러의 데이터에서 수행 할 수 있습니다.
네트워크는 아마도 파이프의 가장 느린 부분 일 것입니다 (수정이 계산 비용이 많이 들지 않는다고 가정함에 따라 소켓 레이어가 이전 데이터를 전송하는 동안 모드를 수행 할 수 있습니다.
Connect/Disternect 빈번한 많은 클라이언트를 처리하는 경우 IO 완료 포트 또는 이와 유사한 메커니즘을 살펴보십시오.