C++ iostream 実装例 その 6
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
]
開始行:
[[C++のiostream]]
* C++ iostream 実装例 その 6 [#l79c52b3]
前節 [[C++ iostream 実装例 その 5]] の考察に基づき、今まで
作成してきた tcpbuffer に connect() と close() を付加しよ...
また、fstream に習って、is_connect() も用意する。
** 修正 2010-01-13 [#pa91b783]
- sync() の return の直前に setp() による位置修正が必要。...
** Windows (winsock) での利用 [#r8d72bd7]
今まで(source が読みづらくなるので)Windows 版を
省略していたが、ここでは全部示す。Windows の時は、compile...
WINSOCK を定義すること。また、getaddrinfo() ではなく
gethostbyname() を使う時は USE_GETHOSTBYNAME を定義するこ...
例えば、MinGW/g++ で両方を定義するのであれば
g++ -c -DWINSOCK -DUSE_GETHOSTBYNAME mytcpbuf.cpp
などとする。
また、winsock は library の load/unload を必要とするので、
netlibrary という class を用意した。main 関数の先頭にでも
object を生成しておく(winsock を使わない時でも dummy と...
働くので、source は共通にできる)。destructor で unload ...
** header file [#u0b544e0]
前節までと異なり、buffer を unsigned char から char に変...
これは、send()、recv() の引数の型が system により異なるた...
header で吸収してもよいのだが、iostream がもともと char ...
ためのものでもあるので、char に変更した。
char 型が signed か
unsigned かは実装依存(だったと思う)なので、int にもって
いく時に符号拡張されないように注意する必要がある。
また、copy constructor と代入 operator は単純に禁止してあ...
(private で宣言して、定義しない)。
// mytcpbuf.h
#ifndef TCPBUFFER_H_INCLUDED
#define TCPBUFFER_H_INCLUDED
#ifdef WINSOCK
#include <winsock2.h>
#include <ws2tcpip.h>
#endif // WINSOCK
#include <streambuf>
namespace mynetlibrary
{
#ifndef WINSOCK
typedef int SOCKET;
#endif // WINSOCK
class netlibrary
{
public:
netlibrary(void);
~netlibrary(void);
private:
static bool m_libloaded;
};
class tcpbuffer :
public std::streambuf
{
public:
tcpbuffer(void);
virtual ~tcpbuffer(void);
bool is_connect(void) const;
int connect(const char* host, int port);
int close(void);
protected:
virtual int underflow(void);
virtual int overflow(int c = std::char_traits<char>::eo...
virtual int sync(void);
private:
tcpbuffer(const tcpbuffer& src);
tcpbuffer& operator=(const tcpbuffer& src);
protected:
#ifndef WINSOCK
static const int INVALID_SOCKET = (-1);
#endif // WINSOCK
static const int MY_MAXGBACK = 128;
static const int MY_GBUFSIZE = 1024;
static const int MY_PBUFSIZE = 1024;
protected:
SOCKET m_socket;
char* m_gbuf;
char m_rbuf[(MY_MAXGBACK + MY_GBUFSIZE + 1)];
char m_pbuf[(MY_PBUFSIZE + 1)];
};
} // namespace mynetlibrary
#endif // TCPBUFFER_H_INCLUDED
** source file [#q5f4faaf]
かなり長くなっているが、connect に、かなりの量を使ってい...
わかるだろう。
しかも connect では getaddrinfo() 版と gethostbyname() 版
を merge しているので、余計長くなっている。
なお、underflow と overflow にある 255 による mask は
char から int への変換で、符号拡張されないようにするため...
// mytcpbuf.cpp
#ifndef WINSOCK
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#endif // WINSOCK
#include "mytcpbuf.h"
namespace mynetlibrary
{
bool
netlibrary::m_libloaded = false;
netlibrary::netlibrary(void)
{
if(m_libloaded)
return;
#ifdef WINSOCK
WORD wVersion;
WSADATA wsaData;
wVersion = MAKEWORD(2,2);
if(::WSAStartup( wVersion, &wsaData ))
m_libloaded = false;
else
m_libloaded = true;
#else // WINSOCK
m_libloaded = true;
#endif // WINSOCK
}
netlibrary::~netlibrary(void)
{
if(!m_libloaded)
return;
#ifdef WINSOCK
::WSACleanup();
#endif // WINSOCK
m_libloaded = false;
}
tcpbuffer::tcpbuffer(void)
{
m_socket = INVALID_SOCKET;
m_gbuf = m_rbuf + MY_MAXGBACK;
setg((char*)m_gbuf, (char*)m_gbuf, (char*)m_gbuf);
setp((char*)m_pbuf, (char*)(m_pbuf + MY_PBUFSIZE));
}
tcpbuffer::~tcpbuffer(void)
{
close();
}
bool
tcpbuffer::is_connect(void) const
{
if(m_socket == INVALID_SOCKET)
return false;
return true;
}
int
tcpbuffer::connect(const char* host, int port)
{
// Returns 0 if success, otherwise returns non-zero.
if(m_socket != INVALID_SOCKET)
return (-1); // already conected
m_socket = ::socket(PF_INET, SOCK_STREAM, 0);
if(m_socket == INVALID_SOCKET)
return (-2);
#ifndef USE_GETHOSTBYNAME
struct sockaddr_in srvaddr;
struct sockaddr_in* resaddr;
struct addrinfo hints;
struct addrinfo* restop;
struct addrinfo* res;
int err;
res = 0;
::memset((char*)&hints, 0, sizeof(hints));
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if((err = ::getaddrinfo(host, 0, &hints, &restop)) != 0)
{
close();
return (-3);
}
res = restop;
while(res)
{
resaddr = (struct sockaddr_in*)res->ai_addr;
::memset((char*)&srvaddr, 0, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = ::htons((u_short)port);
srvaddr.sin_addr = resaddr->sin_addr;
if(::connect(m_socket, (struct sockaddr*)&srvaddr, siz...
break;
res = res->ai_next;
}
::freeaddrinfo(restop);
if(!res)
{
close();
return (-4);
}
#else // USE_GETHOSTBYNAME
struct sockaddr_in srvaddr;
unsigned long inaddr;
struct hostent *hp;
::memset((char*)&srvaddr, 0, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = ::htons((u_short)port);
if((inaddr = ::inet_addr(host)) != INADDR_NONE)
{
::memcpy((char*)&srvaddr.sin_addr, (char*)&inaddr, siz...
if(::connect(m_socket, (struct sockaddr*)&srvaddr, siz...
{
close();
return (-3);
}
}
else
{
if((hp = ::gethostbyname(host)) == 0)
{
close();
return (-4);
}
if(hp->h_addrtype != AF_INET)
{
close();
return (-5);
}
const char** addr = (const char**)hp->h_addr_list;
while(*addr)
{
::memcpy((char*)&srvaddr.sin_addr, (char*)(*addr), hp...
if(::connect(m_socket, (struct sockaddr*)&srvaddr, si...
break;
++addr;
}
if(*addr == 0)
{
close();
return (-6);
}
}
#endif // USE_GETHOSTBYNAME
return 0;
}
int
tcpbuffer::close(void)
{
if(m_socket == INVALID_SOCKET)
return -1;
int ret;
#ifndef WINSOCK
ret = ::close(m_socket);
#else // WINSOCK
ret = ::closesocket(m_socket);
#endif // WINSOCK
m_socket = INVALID_SOCKET;
return ret;
}
int
tcpbuffer::underflow(void)
{
if(m_socket == INVALID_SOCKET)
return std::char_traits<char>::eof();
int c;
if((gptr() >= (char*)m_gbuf) && (gptr() < egptr()))
{
// Why call me ?
// Still we have enough data in the buffer.
c = (int)*gptr(); c &= 255;
return c;
}
// Move the previos (tail) data in backup area.
int nback = (int)(egptr() - eback());
if(nback > MY_MAXGBACK)
nback = MY_MAXGBACK;
if(nback > 0)
::memmove((char*)(m_gbuf - nback), (egptr() - nback), ...
// Fill the new data in read area.
int n;
if((n = ::recv(m_socket, m_gbuf, MY_GBUFSIZE, 0)) <= 0)
return std::char_traits<char>::eof();
setg((char*)(m_gbuf - nback), (char*)m_gbuf, (char*)(m_...
c = (int)m_gbuf[0]; c &= 255;
return c;
}
int
tcpbuffer::overflow(int c)
{
if(m_socket == INVALID_SOCKET)
return std::char_traits<char>::eof();
// Try to make rooms to put a new character
int npend = (int)(pptr() - (char*)m_pbuf);
if(npend < 0)
return std::char_traits<char>::eof();
if(npend > 0)
{
int nsend = ::send(m_socket, m_pbuf, npend, 0);
if(nsend <= 0)
return std::char_traits<char>::eof();
npend -= nsend;
if(npend > 0)
{
// Oooops!! Still we have pending data.
// Move the pending data to the beginning of
// the buffer to send them in next time.
::memmove(m_pbuf, (m_pbuf + nsend), npend);
}
}
setp((char*)(m_pbuf + npend), (char*)(m_pbuf + MY_PBUFS...
if(c == std::char_traits<char>::eof())
return 0;
*pptr() = (char)(c & 255);
pbump(1);
return c;
}
int
tcpbuffer::sync(void)
{
if(m_socket == INVALID_SOCKET)
return (-1);
int nleft = (int)(pptr() - (char*)m_pbuf);
int nsend;
char* p = m_pbuf;
while( nleft > 0 )
{
nsend = ::send(m_socket, p, nleft, 0);
if( nsend <= 0 )
return (-1);
nleft -= nsend;
p += nsend;
}
setp((char*)m_pbuf, (char*)(m_pbuf + MY_PBUFSIZE));
return 0;
}
} // namespace mynetlibrary
** sample program [#k32bfe6e]
さて、今まで windows 版を示してないので、それも兼ねて
sample program を示す。機能は今まで示したものと同じ。
MinGW/g++ で gethostbyname を使うので、あれば
g++ -DWINSOCK -DUSE_GETHOSTBYNAME testbuf2.cpp mytcpbuf....
とすればよい。
// testbuf2.cpp
#include <iostream>
#include "mytcpbuf.h"
char linebuf[4096];
int
main(int argc, char* argv[])
{
mynetlibrary::netlibrary lib;
if(argc != 2)
{
std::cerr << "Usage: " << argv[0] << " host" << std::e...
return (-1);
}
mynetlibrary::tcpbuffer tcp;
tcp.connect(argv[1], 80));
if(!tcp.is_connect())
{
std::cerr << "Tcp connection error." << std::endl;
return (-1);
}
std::iostream tcpstream((std::streambuf*)&tcp);
tcpstream << "GET / HTTP/1.0\r\n";
tcpstream << "Host: " << argv[1] << "\r\n";
tcpstream << "\r\n";
tcpstream.flush();
while(tcpstream.getline(linebuf, 4095))
{
std::streamsize n = tcpstream.gcount();
linebuf[n] = 0;
std::cout << linebuf << std::endl;
}
std::cout.flush();
return 0;
}
終了行:
[[C++のiostream]]
* C++ iostream 実装例 その 6 [#l79c52b3]
前節 [[C++ iostream 実装例 その 5]] の考察に基づき、今まで
作成してきた tcpbuffer に connect() と close() を付加しよ...
また、fstream に習って、is_connect() も用意する。
** 修正 2010-01-13 [#pa91b783]
- sync() の return の直前に setp() による位置修正が必要。...
** Windows (winsock) での利用 [#r8d72bd7]
今まで(source が読みづらくなるので)Windows 版を
省略していたが、ここでは全部示す。Windows の時は、compile...
WINSOCK を定義すること。また、getaddrinfo() ではなく
gethostbyname() を使う時は USE_GETHOSTBYNAME を定義するこ...
例えば、MinGW/g++ で両方を定義するのであれば
g++ -c -DWINSOCK -DUSE_GETHOSTBYNAME mytcpbuf.cpp
などとする。
また、winsock は library の load/unload を必要とするので、
netlibrary という class を用意した。main 関数の先頭にでも
object を生成しておく(winsock を使わない時でも dummy と...
働くので、source は共通にできる)。destructor で unload ...
** header file [#u0b544e0]
前節までと異なり、buffer を unsigned char から char に変...
これは、send()、recv() の引数の型が system により異なるた...
header で吸収してもよいのだが、iostream がもともと char ...
ためのものでもあるので、char に変更した。
char 型が signed か
unsigned かは実装依存(だったと思う)なので、int にもって
いく時に符号拡張されないように注意する必要がある。
また、copy constructor と代入 operator は単純に禁止してあ...
(private で宣言して、定義しない)。
// mytcpbuf.h
#ifndef TCPBUFFER_H_INCLUDED
#define TCPBUFFER_H_INCLUDED
#ifdef WINSOCK
#include <winsock2.h>
#include <ws2tcpip.h>
#endif // WINSOCK
#include <streambuf>
namespace mynetlibrary
{
#ifndef WINSOCK
typedef int SOCKET;
#endif // WINSOCK
class netlibrary
{
public:
netlibrary(void);
~netlibrary(void);
private:
static bool m_libloaded;
};
class tcpbuffer :
public std::streambuf
{
public:
tcpbuffer(void);
virtual ~tcpbuffer(void);
bool is_connect(void) const;
int connect(const char* host, int port);
int close(void);
protected:
virtual int underflow(void);
virtual int overflow(int c = std::char_traits<char>::eo...
virtual int sync(void);
private:
tcpbuffer(const tcpbuffer& src);
tcpbuffer& operator=(const tcpbuffer& src);
protected:
#ifndef WINSOCK
static const int INVALID_SOCKET = (-1);
#endif // WINSOCK
static const int MY_MAXGBACK = 128;
static const int MY_GBUFSIZE = 1024;
static const int MY_PBUFSIZE = 1024;
protected:
SOCKET m_socket;
char* m_gbuf;
char m_rbuf[(MY_MAXGBACK + MY_GBUFSIZE + 1)];
char m_pbuf[(MY_PBUFSIZE + 1)];
};
} // namespace mynetlibrary
#endif // TCPBUFFER_H_INCLUDED
** source file [#q5f4faaf]
かなり長くなっているが、connect に、かなりの量を使ってい...
わかるだろう。
しかも connect では getaddrinfo() 版と gethostbyname() 版
を merge しているので、余計長くなっている。
なお、underflow と overflow にある 255 による mask は
char から int への変換で、符号拡張されないようにするため...
// mytcpbuf.cpp
#ifndef WINSOCK
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#endif // WINSOCK
#include "mytcpbuf.h"
namespace mynetlibrary
{
bool
netlibrary::m_libloaded = false;
netlibrary::netlibrary(void)
{
if(m_libloaded)
return;
#ifdef WINSOCK
WORD wVersion;
WSADATA wsaData;
wVersion = MAKEWORD(2,2);
if(::WSAStartup( wVersion, &wsaData ))
m_libloaded = false;
else
m_libloaded = true;
#else // WINSOCK
m_libloaded = true;
#endif // WINSOCK
}
netlibrary::~netlibrary(void)
{
if(!m_libloaded)
return;
#ifdef WINSOCK
::WSACleanup();
#endif // WINSOCK
m_libloaded = false;
}
tcpbuffer::tcpbuffer(void)
{
m_socket = INVALID_SOCKET;
m_gbuf = m_rbuf + MY_MAXGBACK;
setg((char*)m_gbuf, (char*)m_gbuf, (char*)m_gbuf);
setp((char*)m_pbuf, (char*)(m_pbuf + MY_PBUFSIZE));
}
tcpbuffer::~tcpbuffer(void)
{
close();
}
bool
tcpbuffer::is_connect(void) const
{
if(m_socket == INVALID_SOCKET)
return false;
return true;
}
int
tcpbuffer::connect(const char* host, int port)
{
// Returns 0 if success, otherwise returns non-zero.
if(m_socket != INVALID_SOCKET)
return (-1); // already conected
m_socket = ::socket(PF_INET, SOCK_STREAM, 0);
if(m_socket == INVALID_SOCKET)
return (-2);
#ifndef USE_GETHOSTBYNAME
struct sockaddr_in srvaddr;
struct sockaddr_in* resaddr;
struct addrinfo hints;
struct addrinfo* restop;
struct addrinfo* res;
int err;
res = 0;
::memset((char*)&hints, 0, sizeof(hints));
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if((err = ::getaddrinfo(host, 0, &hints, &restop)) != 0)
{
close();
return (-3);
}
res = restop;
while(res)
{
resaddr = (struct sockaddr_in*)res->ai_addr;
::memset((char*)&srvaddr, 0, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = ::htons((u_short)port);
srvaddr.sin_addr = resaddr->sin_addr;
if(::connect(m_socket, (struct sockaddr*)&srvaddr, siz...
break;
res = res->ai_next;
}
::freeaddrinfo(restop);
if(!res)
{
close();
return (-4);
}
#else // USE_GETHOSTBYNAME
struct sockaddr_in srvaddr;
unsigned long inaddr;
struct hostent *hp;
::memset((char*)&srvaddr, 0, sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = ::htons((u_short)port);
if((inaddr = ::inet_addr(host)) != INADDR_NONE)
{
::memcpy((char*)&srvaddr.sin_addr, (char*)&inaddr, siz...
if(::connect(m_socket, (struct sockaddr*)&srvaddr, siz...
{
close();
return (-3);
}
}
else
{
if((hp = ::gethostbyname(host)) == 0)
{
close();
return (-4);
}
if(hp->h_addrtype != AF_INET)
{
close();
return (-5);
}
const char** addr = (const char**)hp->h_addr_list;
while(*addr)
{
::memcpy((char*)&srvaddr.sin_addr, (char*)(*addr), hp...
if(::connect(m_socket, (struct sockaddr*)&srvaddr, si...
break;
++addr;
}
if(*addr == 0)
{
close();
return (-6);
}
}
#endif // USE_GETHOSTBYNAME
return 0;
}
int
tcpbuffer::close(void)
{
if(m_socket == INVALID_SOCKET)
return -1;
int ret;
#ifndef WINSOCK
ret = ::close(m_socket);
#else // WINSOCK
ret = ::closesocket(m_socket);
#endif // WINSOCK
m_socket = INVALID_SOCKET;
return ret;
}
int
tcpbuffer::underflow(void)
{
if(m_socket == INVALID_SOCKET)
return std::char_traits<char>::eof();
int c;
if((gptr() >= (char*)m_gbuf) && (gptr() < egptr()))
{
// Why call me ?
// Still we have enough data in the buffer.
c = (int)*gptr(); c &= 255;
return c;
}
// Move the previos (tail) data in backup area.
int nback = (int)(egptr() - eback());
if(nback > MY_MAXGBACK)
nback = MY_MAXGBACK;
if(nback > 0)
::memmove((char*)(m_gbuf - nback), (egptr() - nback), ...
// Fill the new data in read area.
int n;
if((n = ::recv(m_socket, m_gbuf, MY_GBUFSIZE, 0)) <= 0)
return std::char_traits<char>::eof();
setg((char*)(m_gbuf - nback), (char*)m_gbuf, (char*)(m_...
c = (int)m_gbuf[0]; c &= 255;
return c;
}
int
tcpbuffer::overflow(int c)
{
if(m_socket == INVALID_SOCKET)
return std::char_traits<char>::eof();
// Try to make rooms to put a new character
int npend = (int)(pptr() - (char*)m_pbuf);
if(npend < 0)
return std::char_traits<char>::eof();
if(npend > 0)
{
int nsend = ::send(m_socket, m_pbuf, npend, 0);
if(nsend <= 0)
return std::char_traits<char>::eof();
npend -= nsend;
if(npend > 0)
{
// Oooops!! Still we have pending data.
// Move the pending data to the beginning of
// the buffer to send them in next time.
::memmove(m_pbuf, (m_pbuf + nsend), npend);
}
}
setp((char*)(m_pbuf + npend), (char*)(m_pbuf + MY_PBUFS...
if(c == std::char_traits<char>::eof())
return 0;
*pptr() = (char)(c & 255);
pbump(1);
return c;
}
int
tcpbuffer::sync(void)
{
if(m_socket == INVALID_SOCKET)
return (-1);
int nleft = (int)(pptr() - (char*)m_pbuf);
int nsend;
char* p = m_pbuf;
while( nleft > 0 )
{
nsend = ::send(m_socket, p, nleft, 0);
if( nsend <= 0 )
return (-1);
nleft -= nsend;
p += nsend;
}
setp((char*)m_pbuf, (char*)(m_pbuf + MY_PBUFSIZE));
return 0;
}
} // namespace mynetlibrary
** sample program [#k32bfe6e]
さて、今まで windows 版を示してないので、それも兼ねて
sample program を示す。機能は今まで示したものと同じ。
MinGW/g++ で gethostbyname を使うので、あれば
g++ -DWINSOCK -DUSE_GETHOSTBYNAME testbuf2.cpp mytcpbuf....
とすればよい。
// testbuf2.cpp
#include <iostream>
#include "mytcpbuf.h"
char linebuf[4096];
int
main(int argc, char* argv[])
{
mynetlibrary::netlibrary lib;
if(argc != 2)
{
std::cerr << "Usage: " << argv[0] << " host" << std::e...
return (-1);
}
mynetlibrary::tcpbuffer tcp;
tcp.connect(argv[1], 80));
if(!tcp.is_connect())
{
std::cerr << "Tcp connection error." << std::endl;
return (-1);
}
std::iostream tcpstream((std::streambuf*)&tcp);
tcpstream << "GET / HTTP/1.0\r\n";
tcpstream << "Host: " << argv[1] << "\r\n";
tcpstream << "\r\n";
tcpstream.flush();
while(tcpstream.getline(linebuf, 4095))
{
std::streamsize n = tcpstream.gcount();
linebuf[n] = 0;
std::cout << linebuf << std::endl;
}
std::cout.flush();
return 0;
}
ページ名: