C++のiostream

stream 型入出力

Stream 型入出力に対する streambuf を考える。

入力

入力は backup sequence を持たせなければ 比較的容易であると思われるが、backup sequence が無いと delimter 付きの 関数が失敗する可能性がある。そこで、ここでは backup sequence を持つ実装を考える。

  • Backup sequence の最小保持文字数を minback とする。
  • 用意する buffer の先頭を表すポインタを pbgn とする。
  • 用意する buffer の大きさを bufsize とする。bufsize > minback でなければならない。
  • pend = pbgn + busize とする。
  • 初期状態では eback() = gptr() = egptr() = pbgn に設定する。
  • underflow では
    • egptr - pbgn > minback の時、d = egptr() - pbgn - minback として、pbgn + d から egptr() 未満の範囲の文字列を pbgn から egptr() - d 未満の範囲へ移動する。その後、gptr() を gptr() - d に、また egptr() を egptr() - d に設定し、次項へ進む。
    • egptr() から pend までを入力列から読み込む。読み込んだ文字数を n とする時、egptr() を egptr() + n に設定する。

出力

出力は overflow() を上書きする。ただし、残余がある場合も想定する。出力文字が traits::eof() 以外に指定された場合、バッファ出力後にその文字をバッファに入れる。 従って、この場合、完全には空にならない。

完全にバッファを出力したい場合は、 ostream で flush() を呼ぶ。ostream の flush() は省略時設定で、streambuf の sync() を呼ぶことになっているので、sync() を実装する。

実装例

Socket descriptor s に対する入力 stream 用の streambuf 派生 class、Sockbuf を 作ってみる。

この class が s に対して行う操作は recv() のみとする。 Socket descriptor の(dup() を使った)複製も行わない。 すなわち、connect()、close()、accept() など recv() 以外の操作は、 すべてこの class の外で行うものとする。

ヘッダファイル sockbuf.h

#ifndef SOCKBUF_H_INCLUDED
#define SOCKBUF_H_INCLUDED

#include <iostream>

class Sockbuf : public std::streambuf
{
public:
	Sockbuf(int s);
	~Sockbuf();

protected:
	virtual int sync();
	virtual int underflow();
	virtual int overflow(int c = std::char_traits<char>::eof());

private:
	static const int minback = 256;
	static const int rbufsiz = 768;
	static const int wbufsiz = 512;
	int m_sock;
	char m_rbuf[rbufsiz];
	char m_wbuf[wbufsiz];
};

#endif	/* SOCKBUF_H_INCLUDED */

本体 sockbuf.cpp

#include "sockbuf.h"
#include <winsock2.h>
using namespace std;

Sockbuf::Sockbuf(int s)
{
	m_sock = s;
	setg(m_rbuf,m_rbuf,m_rbuf);
	setp(m_wbuf,m_wbuf + wbufsiz);
}

Sockbuf::~Sockbuf()
{
}

int
Sockbuf::underflow()
{
	if(egptr() > m_rbuf + minback)
	{
		int d = egptr() - m_rbuf - minback;
		char* s = m_rbuf;
		while(s < (egptr() - d))
		{
			*s = *(s + d);
			++s;
		}
		setg(m_rbuf, gptr() - d, egptr() - d);
	}
	int m = rbufsiz - (egptr() - m_rbuf);
	int n = recv(m_sock, egptr(), m, 0);

	if(n <= 0)
		return (int)char_traits<char>::eof();
	setg(m_rbuf, gptr(), egptr() + n); 
	return (int)*gptr();
}

int
Sockbuf::sync()
{
	if(pbase())
	{
		int nleft = pptr() - pbase();
		char* buf = pbase();
		while(nleft > 0)
		{
			int nsent = send(m_sock, buf, nleft, 0);
			if(nsent <= 0)
				return (-1);
			nleft -= nsent;
			buf += nsent;
		}
	}
	setp(m_wbuf, m_wbuf + wbufsiz);
	return 0;
}

int
Sockbuf::overflow(int c)
{
	if(pbase())
	{
		int d = pptr() - pbase();
		if(d > 0)
		{
			int n = send(m_sock, pbase(), d, 0);
			if(n <= 0)
				return (int)char_traits<char>::eof();
			char* s = pbase();
			while((s + n) < pptr())
			{
				*s = *(s + n);
				++s;
			}
			pbump(-n);
		}
		else
			setp(m_wbuf, m_wbuf + wbufsiz);
		if(c != char_traits<char>::eof())
		{
			*pptr() = (char)c;
			pbump(1);
			return c;
		}
		return (int)char_traits<char>::not_eof(c);
	}
	return (int)char_traits<char>::eof();
}

使用例

TCP接続用 C library と組み合わせた例を示す。Source file 名を exam.cpp とする時、Windows の場合

gcc -c -DWIN32 tcplib.c
g++ -DWIN32 exam.cpp sockbuf.cpp tcplib.o -lws2_32 -oexam.exe

使用例1

サーバに接続して、文字を読み出し表示する。切断はサーバ側から行うものとする。

#include "sockbuf.h"
#include "tcplib.h"

using namespace std;

int main(int argc, char* argv[])
{
	int port;
	SOCKET s;

	if(argc != 3)
	{
		cerr << "Usage " << argv[0] << " host port" << endl;
		return (-1);
	}
	port = atoi(argv[2]);
	netlibstart();
	s = tcpsocket();
	tcpopen(s, argv[1], port);

	Sockbuf mybuf(s);
	std::istream mystream(&mybuf);

	char c;
	while(mystream.get(c))
		cout << c;

	tcpclose(s);
	netlibstop();
	return 0;
}

使用例2

Http クライアントもどき。Host port を指定すると、そのルートドキュメントを 要求し、応答を表示する。

#include "sockbuf.h"
#include "tcplib.h"

using namespace std;

int main(int argc, char* argv[])
{
	int port;
	SOCKET s;

	if(argc != 3)
	{
		cerr << "Usage: " << argv[0] << " host port" << endl;
		return (-1);
	}
	port = atoi(argv[2]);
	netlibstart();
	s = tcpsocket();
	tcpopen(s, argv[1], port);

	Sockbuf mybuf(s);
	std::iostream mystream(&mybuf);

	mystream << "GET / HTTP/1.0";
	mystream.put(0x0d);	// CR
	mystream.put(0x0a);	// LF
	mystream.put(0x0d);	// CR
	mystream.put(0x0a);	// LF
	mystream.flush();

	char c;
	while(mystream.get(c))
		cout << c;

	tcpclose(s);
	netlibstop();
	return 0;
}

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2008-01-15 (火) 11:50:51