ログイン

2016年3月 弊社ホームページは新しくなりました。 https://thinkridge.com

メインメニュー

携帯公式サイト


携帯電話をもっと便利に
もっと楽しく


史上初の吹奏楽専門着メロサイト


POPで癒しでライトでとんがって気持ちのいい〜オルゴール着メロをあなたに

Magome

クラウドベースの MIDI シーケンサ Magome

音楽制作に興味のある方を対象に、スタンドアロンでも使え、ネットならではの面白さも兼ね備えた音楽制作アプリの提供を目指しています

for 携帯電話

https://thinkridge.com/m/
ケータイはこちらへ

2015年11月7日(土曜日)

SNI SSL ってのがけっこう一般的になってきてるみたい

カテゴリー: - takatsuka @ 16時33分22秒

SNI SSL ってのがけっこう一般的になってきてるみたいで、これはかなり良さげな気がしてます。

SSL っていうと IP に紐づけるもんで、いわゆるレンタルサーバーの1サイトで使うみたいなことは事実上不可。
・・・っていうのは、ちょっと前までの話。今はそんなことなさげです。

なので、HTTP じゃ不安だ(=信用ない)から HTTPS にしたい!ってなったら1つのIPを占有できる専用サーバー(今だとVPSとかも)を使うのが定石だったのが、SNI SSL を使えば、レンタルサーバーでも HTTPS のメリットを受諾できます。

DB が使えて、cron が使えて、perl や PHP が使えるレンタルサーバーは全然珍しくないので、そこに SNI SSL を組み合わせれば、実は、Webシステム案件の多くは、証明書代含めても月々千円程度の環境で行けてしまいます。

さらに、レンタルサーバーであれば、OSのパッチあてとかメールのウイルスフィルタ対応とかは、業者様がやってくれ(ることになって)ます。

そのへんも勘案すると実は、保守費用を別にして月々数千円〜1万円以上を覚悟しないといけないと思っていたことは正しくないかもしれません。
ぜひ考慮したいところです。

とはいえ、もちろん、それでも採用出来ないケースはあります。
業者のサーバーに機密情報を置きたくないとか、
ネイティブアプリをサーバーで動かしたいとか、
WindowsXP(のIE) とかガラケーとかは SNI SSL に非対応みたいなので、もしそれら端末からのアクセスを考慮する必要があるときとか。

なにはともあれ、選択肢や可能性が増えることには違いないので、こういう新技術は上手に使っていくことが必要だって感じたと同時に、付いていけるように勉強しなきゃならないと思った次第です。
頑張ります!・・頑張りたい!・・頑張なくてもいい天賦の才がほしい!・・来世に期待しよう!

ありがとうございました。


2015年8月31日(月曜日)

google maps api 表示されなくなってた

カテゴリー: - takatsuka @ 11時45分33秒

ふと数日前くらいからだと思うけど、google maps api の kml layer で、デフォルトのマーカー(赤い下向き矢印っぽいやつ)が表示されてなくなってた。カスタムのマーカーは問題なく表示される。
コードは何も変えていないのになんで?と思って調査。

kml の <iconstyle><color>…</color> が存在するだけで、表示が出来なくなってるようだ。
とはいえコレ、google map でエクスポートした google 様が返してくる kml なんだけど・・。

幸い、使っているサイトは、サーバーを介して kml をクライアントに渡す作りなので、サーバー側で小細工が可能。
というわけで暫定対策として、問題のタグを全削除して出力するようにして対策。
マーカーに色がつかないけど、なにも表示されないよりはいいでしょう。

ただ、原因がまったく不明。ご存じの方おられましたらご助言賜り度。


2015年8月9日(日曜日)

VMware Server から ESXi へ

カテゴリー: - takatsuka @ 12時59分06秒

社内サーバー環境刷新してみました。

これまでは、1台のサーバーPCで、WindowsServer をホストOSにして、VMwareServer1.0 使ってゲストOSを5〜6個動かしておりました。

これはこれでまったく困ってなかったんですが、もう提供すらされてない VMwareServer を使い続けても芸がないかと思い、勉強かねて FreeNAS と ESXi を使った環境に移行してみました。

貧乏性 エコな性分なので、これまでどおり全てを激安サーバー1台で賄うようにして、初期投資と電気代と場所の節約を念頭に置きます。

サーバーPC は FUJITSU MX130 S2。HDD は 1T×2台。ESXi のブート用に8Gの USB メモリ。

  1. ESXi6.0 の ISO イメージをダウンロードして、USBメモリにブート可能な状態として用意。
  2. MX130 S2 に USBメモリを挿し、BIOS で USB ブート可に変更し、ESXi6.0 のインストーラーを起動。
  3. インストール先に、同じUSBメモリを指定しインストール。
    ESXi6.0 は MX130 S2 のデバイスはたぶん全て認識してくれたので、特別なことは何もなくインストール完了。
  4. ESXi 上で FreeNAS9.3 に HDD の 8G くらいを割り当ててインストール。
  5. FreeNAS に、HDD×2 の残りを割り当てる。大体 900G × 2。
  6. FreeNAS 上で、この HDD のミラーのボリュームを作り、iSCSI で共有とする。その際、暗号化をONにして、パスフレーズも設定。
  7. ESXi に戻って、この iSCSI を使ったデータストアを作成。これで、900Gサイズの暗号化されてて RAID1(ミラー)な領域を確保。
  8. これまで稼働させていた各ゲストOSを、VMware vCenter Converer Standalone を使って、上記の領域に持っていって(変換して)完了。

という感じです。

この環境の1番のメリットとしては、FreeNAS のボリュームの中に、各ゲストOSを置くことになるので、特別なハードは不要な上、各ゲストOSに何の細工もナシで、RAID と HDD 暗号化と圧縮の恩恵に預かれるところでしょうか。

HDD 故障に備えた RAID はもちろんですが、昨今の風潮的に、社内サーバーとはいえ泥棒に入られりして HDD を盗まれる可能性も考慮して、暗号化は入れときたいところです。

さらに、暗号化しておけば、HDD を破棄するときの厄介からも解放されます。
データ流出を恐れて HDD にドリルで穴開けるとか、専門業者に高いお金だして消去と廃棄を依頼するとか、かなり手間をかけなければならないところ、何も考えずにそのまま廃棄出来てしまうのはかなりメリットです。

デメリットとしては、FreeNAS がけっこうメモリを食うようなので、そこは奢っとく必要がありますが、ミラーで使う分にはそれほどでもないっぽい気もします。

もうちょっとゴージャスを求めていいんであれば、RAIDZというやつにしてもいいかもです。 HDD が最低3台必要で、メモリも食いますが、すごそうです。

一応、すんなり行かなかったこともあり、ネットで調べたりしたので、備忘録としてあげておきます。情報をあげていらっしゃる方々に感謝です。

  • VPN (ブリッジ) 通信が通らない対策。
    ローカルブリッジ機能を使用するためにはプロミスキャスモードを有効にする必要があるが、ESXi のスイッチがデフォルトで無効になってるので変更する。
    スイッチのセキュリテイタブより「無差別モード」を「承諾」に変更して「OK」をクリック
  • ESXi に WindowsXP,2003 の vSphere Client から接続できない場合の対処
    1. ESXi へ SSH にて接続
      /etc/vmware/rhttpproxy/config.xml を編集

      <vmacore> - <ssl> -

      <cipherList>ALL</cipherList> ←追記

    2. サービスを再起動
      /etc/init.d/rhttpproxy restart
  • VMware vCenter Converer Standalone で変換する際、デフォルトのままだと、元の HDD の設定に関わらず シックプロビジョニング になってしまうので、空き容量に余裕がないときは要設定変更。

これだけ便利でよくできた環境が、たいしてコストかけずに出来てしまうことに感心してしまいますし、優秀なソフトを開発されてる方々に感謝です。

いまどきは、クラウドやら Saas やら VPS やらが充実してますので、社内にサーバーやストレージを置かなきゃいけないようなケースも減ってきているとは思いますが、大量のデータを扱いたいとか、外のサーバーに大事なデータを置いておくのは不安とか、コストの面以外にも、社内に置くメリットはまだあると思います。

一応自分もIT業界の端っこに身を置いてますんで、いろいろな案件に適切な提案をしていけるよう精進しとかんとイカンなと思った次第です。


2015年2月1日(日曜日)

boost::asio で UDPホールパンチング

カテゴリー: - takatsuka @ 22時21分43秒

NAT 越えの技術は skype が出てきたころには話題になってる気がしますが、自前のソフトに組み込んで使えるような標準的な形に既になってるのかよくわからないってこともあり、興味本位で試してみました。

UDP ホールパンチングによる NAT 越えです。
で、boost::asio の udp の勉強も兼ねてサンプルアプリを作ってみました。

とはいえ、boost::asio の udp については難しいことは何もなく tcp よりシンプルに使えました。有難いことです。開発者の方に感謝です。

さて、UDP ホールパンチングについて。
wikipedia みると NAT の実装にはいろいろあるみたいですが、大ざっぱな分類でいう Port-Restricted cone NAT という環境同士までの P2P 通信は実現出来ました。
Full cone NAT とか Address-Restricted cone NAT の環境は試せてないんですが、Port-Restricted より条件緩いものなので、これらもたぶん問題なさそう。

で、Symmetric NAT って言われる環境はやっぱり無理そうでした。

docomo の LTE回線、純正のSPモード と MVNO (nifty の一番安いヤツ) を試しましたが、これに該当してそうです。
スマホのテザリングで繋げた PC で試したので、Symmetric NAT がプロバイダ側にあるのか、スマホのテザリング機能なのか判断できませんが残念。早々に諦めました。

で、以下は自分自身向けの備忘録もかねて、コードの抜粋と、軽く説明を・・。

  • ヘッダ
namespace RLib
{

    class CUdpSocket         :private boost::noncopyable     {         class CSocket;         const boost::shared_ptr<CSocket>    m_spSocket;     public:         CUdpSocket(asio::io_service &ioService);         ~CUdpSocket();         void Close();         bool Bind(unsigned short nPort,boost::system::error_code &ec=boost::system::error_code());         bool Connect(const std::string &sDomain,const std::string &sPort);         bool SendTo(const boost::asio::ip::udp::endpoint &endPoint,                     const boost::shared_ptr<const vector<char>> &spData,                     boost::system::error_code &ec=boost::system::error_code());         bool Send(const boost::shared_ptr<const vector<char>> &spData,                   boost::system::error_code &ec=boost::system::error_code());

        // データ受信         // ・次の読み込み待ちにする場合には Receive(); をコールすべし。コールしない場合には何もしない。         typedef boost::function<void (CUdpSocket &udpSocket,const boost::system::error_code &ec,                                 const boost::asio::ip::udp::endpoint &endPointRemote,                                 const boost::shared_ptr<const vector<char>> &spReceivedData)> FuncOnReceived;         bool Receive(const FuncOnReceived &funcOnReceived,unsigned short nBufferSize=8192);

    public:         static CString GetTextAddress(const asio::ip::udp::socket::endpoint_type &ep)             {                 return CRString::Format(_T("%s:%d"),CString(ep.address().to_string().c_str()),ep.port());             }     };

}

class CUdpHolepunching {     class CMain;     CMain   &m_main; public:     typedef boost::function<void (const CString &s)> FuncMessage;     CUdpHolepunching(const FuncMessage &funcMessage);     ~CUdpHolepunching();

    void Server(unsigned short nPort);     void Client(const std::string &sDomain,const std::string &sPort);

};

  • .cpp
class CUdpSocket::CSocket
{
public:
    CUdpSocket      *m_pUdpSocket;
    ip::udp::socket m_socket;
public:
    CSocket(CUdpSocket &udpSocket,io_service &ioService)
        :m_pUdpSocket(&udpSocket)
        ,m_socket(ioService)
        {
            m_socket.open(ip::udp::v4());
        }
};

CUdpSocket::CUdpSocket(asio::io_service &ioService) :m_spSocket(new CSocket(*this,ioService)) { }

CUdpSocket::~CUdpSocket() {     m_spSocket->m_pUdpSocket = NULL;        // 破棄されたマーク }

void CUdpSocket::Close() {     m_spSocket->m_socket.close(); }

bool CUdpSocket::Bind(unsigned short nPort,boost::system::error_code &ec) {     return m_spSocket->m_socket.bind( ip::udp::endpoint(ip::udp::v4(),nPort), ec ) == false;    // エラーなら }

bool CUdpSocket::Receive(const FuncOnReceived &funcOnReceived,unsigned short nBufferSize) {     struct F{         static void OnReceived(const boost::system::error_code &ec,size_t bytesReceived,                                boost::shared_ptr<CSocket> spSocket,                                boost::shared_ptr<ip::udp::endpoint> spEndpointRemote,                                boost::shared_ptr<vector<char>> spBuffer,const FuncOnReceived funcOnReceived)         {             if( !spSocket->m_pUdpSocket ) return;                       // 破棄されてる?             if( ec ){   // Error                 if( ec == boost::asio::error::operation_aborted ) return;                 //return; エラーでもコールはする             }

            spBuffer->resize(bytesReceived);             if( funcOnReceived ){                 funcOnReceived( *spSocket->m_pUdpSocket, ec, *spEndpointRemote, spBuffer );             }         }     };

    boost::shared_ptr<ip::udp::endpoint> spEndpoint(new ip::udp::endpoint);     boost::shared_ptr<vector<char>> spBuffer(new vector<char>(nBufferSize));    // 受信バッファ     m_spSocket->m_socket.async_receive_from(         asio::buffer(*spBuffer),         *spEndpoint,         boost::bind(             &F::OnReceived,             boost::asio::placeholders::error,             boost::asio::placeholders::bytes_transferred,             m_spSocket,spEndpoint,spBuffer,funcOnReceived)     );

    return true; }

bool CUdpSocket::SendTo(const boost::asio::ip::udp::endpoint &endPoint,                         const boost::shared_ptr<const vector<char>> &spData,boost::system::error_code &ec) {     if( !spData ){         BOOST_ASSERT(false);         return false;     }     const size_t size = m_spSocket->m_socket.send_to( asio::buffer(*spData), endPoint, 0, ec );     if( !ec ) return size == spData->size();     ATLTRACE( _T("nCUdpSocket Error Send -> %s"), CString(ec.message().c_str()) );     return false; }

bool CUdpSocket::Send(const boost::shared_ptr<const vector<char>> &spData,boost::system::error_code &ec) {     if( !spData ){         BOOST_ASSERT(false);         return false;     }     const size_t size = m_spSocket->m_socket.send( asio::buffer(*spData), 0, ec );     if( !ec ) return size == spData->size();     ATLTRACE( _T("nCUdpSocket Error Send -> %s"), CString(ec.message().c_str()) );     return false; }

////////////////////////////////////////////////

class CUdpHolepunching::CMain     :public CWindowImpl<CMain> { public:     DECLARE_WND_CLASS( _T("CMain") );     BEGIN_MSG_MAP(CMain)         MESSAGE_HANDLER(WM_TIMER, OnTimer)     END_MSG_MAP() public:     LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&)         {             m_ioService.poll();             return 0;         } public:     const CRUid                 m_id;     FuncMessage                 m_funcMessage;     io_service                  m_ioService;     CUdpSocket                  m_udpSocket;     auto_ptr<deadline_timer>    m_apTimer;

#pragma pack(push,1)     struct CEndpoint     {         unsigned long   m_nIpv4;         unsigned short  m_nPort;         CEndpoint()             {}         CEndpoint(const ip::udp::endpoint &endPoint)             {                 m_nIpv4 = endPoint.address().to_v4().to_ulong();                 m_nPort = endPoint.port();             }     }; #pragma pack(pop)

    std::map<CRUid,CEndpoint>   m_mapMember;

    void OnReceived(CUdpSocket &udpSocket,const boost::system::error_code &ec,                     const boost::asio::ip::udp::endpoint &endPointRemote,                     const boost::shared_ptr<const vector<char>> &spBuffer)         {             if( ec ){   // Error                 m_funcMessage( CRString::Format(_T("%s : OnRecived Error [%s] %s"),                                CString(m_id.GetText().c_str()), CUdpSocket::GetTextAddress(endPointRemote),                                CString(ec.message().c_str())) );             }else{                 m_funcMessage( CRString::Format(_T("%s : OnRecived [%s]"), CString(m_id.GetText().c_str()),                                CUdpSocket::GetTextAddress(endPointRemote) ) );

                // メンツリスト更新                 vector<char> vBuffer(*spBuffer);                 if( vBuffer.size() >= sizeof(CRUid) ){ // このソケットの送り主を自身のメンツリストにマージ                     CRUid id;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(id),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&id),sizeof(id)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(id) );                     m_mapMember[id] = CEndpoint(endPointRemote);                 }else BOOST_ASSERT(false);                 // 送り主からのメンツリストを自身のメンツリストにマージ                 while( vBuffer.size() >= sizeof(CRUid)+sizeof(CEndpoint) ){                     CRUid id;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(id),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&id),sizeof(id)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(id) );                     CEndpoint ep;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(ep),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&ep),sizeof(ep)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(ep) );                     if( id != m_id ){                                       // 自分自身は除く                         m_mapMember[id] = ep;                     }                 }

            }

            udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) );    // 次の読み込み待ちを開始         }

public:     CMain(const FuncMessage &funcMessage)         :m_id(CRUid::RandomGenerator())         ,m_funcMessage(funcMessage)         ,m_udpSocket(m_ioService)         ,m_apTimer(new deadline_timer(m_ioService))         {             Create(HWND_MESSAGE,CRect(0,0,0,0),_T(""),NULL);             ::SetTimer( m_hWnd, 0, 1, NULL );              m_apTimer->expires_from_now( boost::posix_time::millisec(0) );          // nTime秒後に             m_apTimer->async_wait( boost::bind( &CMain::OnDeadlineTimer, this, _1 ) );         }

    ~CMain()         {             m_apTimer.reset();             m_udpSocket.Close();             if( m_hWnd ) DestroyWindow();         }

    void OnDeadlineTimer(const boost::system::error_code& error)         {             if( error || error==boost::asio::error::operation_aborted ) return; // チェック

            if( m_apTimer.get() ){                      // 終了していないなら次のタイマーを設定                 m_apTimer->expires_at( m_apTimer->expires_at() + boost::posix_time::millisec(5000) );                 m_apTimer->async_wait( boost::bind( &CMain::OnDeadlineTimer, this, _1 ) );             }

            // メンツリスト送信             boost::shared_ptr<vector<char>> spData(new vector<char>);             std::copy( reinterpret_cast<const char*>(&m_id), reinterpret_cast<const char*>(&m_id)+sizeof(m_id),                        std::back_inserter(*spData) );             for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapMember.begin(); i!=m_mapMember.end(); i++ ){                 const CRUid &id = i->first;                 std::copy( reinterpret_cast<const char*>(&id), reinterpret_cast<const char*>(&id)+sizeof(id),                            std::back_inserter(*spData) );                 const CEndpoint &ep = i->second;                 std::copy( reinterpret_cast<const char*>(&ep), reinterpret_cast<const char*>(&ep)+sizeof(ep),                            std::back_inserter(*spData) );             }

            CString sSendTo;             for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapMember.begin(); i!=m_mapMember.end(); i++ ){                 const CEndpoint &ep = i->second;                 ip::address addr(boost::asio::ip::address_v4(ep.m_nIpv4));                 ip::udp::endpoint endpoint( addr, ep.m_nPort );                 m_udpSocket.SendTo( endpoint, spData );                 sSendTo.Format(_T("%s [%s]"),CString(sSendTo),CUdpSocket::GetTextAddress(endpoint));             }             if( !sSendTo.IsEmpty() ){                 m_funcMessage( CRString::Format(_T("%s : SendTo %s"), CString(m_id.GetText().c_str()), sSendTo ));             }         }

    void Server(unsigned short nPort)         {             boost::system::error_code ec;             if( !m_udpSocket.Bind(nPort,ec) ){                 m_funcMessage( CRString::Format(_T("%s : Bind Error [%s]"), CString(m_id.GetText().c_str()),                                                 CString(ec.message().c_str()) ) );                 return;             }             m_udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) ); // 読み込み待ちを開始         }

    void Client(const std::string &sDomain,const std::string &sPort)         {             boost::shared_ptr<vector<char>> spData(new vector<char>);             std::copy( reinterpret_cast<const char*>(&m_id),                        reinterpret_cast<const char*>(&m_id)+sizeof(m_id), std::back_inserter(*spData) );              ip::udp::resolver resolver(m_ioService);             ip::udp::resolver::query query(ip::udp::v4(), sDomain, sPort );             boost::system::error_code ec;             ip::udp::resolver::iterator iterator = resolver.resolve(query,ec);             if( ec ){                 m_funcMessage( CRString::Format(_T("%s : resolve Error [%s]"),                                               CString(m_id.GetText().c_str()), CString(ec.message().c_str()) ) );                 return;             }             m_udpSocket.SendTo( *iterator,spData );             m_udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) );   // 読み込み待ちを開始         }

};

CUdpHolepunching::CUdpHolepunching(const FuncMessage &funcMessage) :m_main(*new CMain(funcMessage)) { }

CUdpHolepunching::~CUdpHolepunching() {     delete &m_main; }

void CUdpHolepunching::Server(unsigned short nPort) {     m_main.m_ioService.post( boost::bind( &CMain::Server, &m_main, nPort ) ); }

void CUdpHolepunching::Client(const std::string &sDomain,const std::string &sPort) {     m_main.m_ioService.post( boost::bind( &CMain::Client, &m_main, sDomain, sPort ) ); }

UDP ホールパンチングの仕組み上、穴をあけずとも UDP が通る環境が一つ必要です。
STUN サーバーと言われるもの。
サンプルアプリでは「サーバー起動」ってすると、UDP ポートを指定して待ち受け状態になります。
(とはいえ、標準化されてるプロトコルを実装してるわけではなく、オレオレ実装です。)

各端末(クライアントPC)は、このサーバーに接続することで、自身の NAT に穴を開けることと同時に、サーバーから各端末のエンドポイント情報を貰います。
あとは、各端末同士がそのエンドポイントに対して送信し合うことで P2P 通信を行う。という寸法です。

サンプルアプリでは、サーバーもクライアントも、すべての相手に対し、キープアライブも兼ねて、5秒毎に各端末のエンドポイント情報を送信し続けます。

というわけで、NAT 越えの P2P 通信自体は出来ることがわかったんですが、実用的にするのはいろいろハードルがあって、
Symmetric NAT 内の端末に対しては、サーバーなりが橋渡しをして通信しないといけない。とか、
UDP すら通さないような環境向けに TCP で橋渡しするようなサーバーを用意する。とか、
UDP を使うので、TCP 並の信頼性とか順序の安定性が必要なら自前実装しなきゃ。とか、
同じ NAT 内の端末同士は、ローカルIPで通信させるなり対策しなきゃ。(たぶん一般的なルーターでは、自分自身が開けたポートに自分自身から接続は出来なさそうなので)。とか。

この程度は世の P2P アプリ(skypeとか)は当然のように実現してるんだと思います。御見それです。

これらも楽しそうなので作ってみたいですが、仕事かなにかで必要になってくれないと手を出しずらいです。大変そうなので。

上記のソース一式はこちらからお願いします。例によって windows 用です。MFC 使ってます。
なにか間違いとかご意見とかればぜひお願いします。

ありがとうございました。


2014年11月3日(月曜日)

移転のご案内

カテゴリー: - ohashi @ 18時30分12秒

この度、有限会社シンクリッジは千葉市中央区よりさいたま市大宮区へ移転致しました。
今後ともご支援ご鞭撻の程、よろしくお願い申し上げます。

新住所
〒330-0854
さいたま市大宮区桜木町4−929−2桜の木ヨシノ102

電話番号
048-729-7045


2014年9月8日(月曜日)

Magome に MML の機能を入れてみました

カテゴリー: - takatsuka @ 11時53分43秒
  • クラウドMIDIシーケンサ Magome に MML 打ち込みの機能を入れてみました。
    昨今のシーケンサというかDAWでは、ステップ入力自体があんまりなさそうに思うんですが、グラフィカルで直観的なUIと、テキストでカタカタと打っていく方式を組み合わせると、けっこう便利なんじゃなかろうかと思ってたところ、確かに、テキストで打ち込んだ音符がその場でピアノロールで表示されるっていうのは、気分いいです。
  • ステップ入力といえばレコンポーザが有名だったと思うんですが、実はちゃんと使ったことはないです。自分の場合、打ち込みは BASIC 言語から入って、その流れで MML 打ち込みが普通になって、それから MAC で Studio Vision を使い始めたので、王道のステップ入力というのをよく知りません。
  • ということもあり MML です。MML が ステップ入力 というカテゴリに入るのかわかりませんが、キーボード(鍵盤ではなくPCのキーボード)で打ち込むというのは慣れれば結構ラクだと思うんです。それに、書式の工夫次第で可能性が広がりそうだし。
  • 関数をこさえて呼べばフレーズが勝手に鳴るとか、スケール名を入れると音符が勝手にそのスケールにアジャストされる。とか、サビのキーが高すぎときには、その手前で3度くらいスムーズに下げるような和声学に基づいたモーションを生成してくれる。とか。
  • と、アイデアだけは広がるんですが、実現させて使ってみないとどういうものかわからない。頭の中だけで完成形をイメージできない。ってのが、自分の想像力不足かなと思います。
  • 形にするのが一番大変なとこでもあり、楽しいとこでもあるんで、徐々に進めてこうと思います。ご助言ご意見などあればぜひお願いいたします。

2014年7月24日(木曜日)

magome のサイトを xoops cube で刷新

カテゴリー: - takatsuka @ 00時21分51秒

magome のサイトを xoops cube を使ったものに刷新しました。

ユーザー登録&管理の仕組みを自前で組むのは手間かかりそうだし、便利なプラグイン(モジュール)がそのまま使えるのはラクだなという理由です。 ラクな分カユいとこに手が届きづらくなるデメリットはきっとあると思うんですが、それを帳消しに出来るほどのメリットがあると思いました。開発者の方々には感謝です。

で、今後もモジュール開発をするであろう主に未来の自分用にメモを残しておこうと思いました。間違いとかあればぜひご指摘頂ければ幸いです。

ノウハウメモ

DB

  • テーブル作成は、"モジュール/sql/mysql.sql” を用意して SQL を書く。それを、xoops_version.php の$modversion[’sqlfile’][’mysql’] = “sql/mysql.sql”; って指定。
    • なので、それを反映させるには、モジュールの再インストールが必要。
    • なお、$modversion[’tables’][index] = “テーブル名” への反映もしないと、モジュールアンインストール時にテーブルを削除してくれない。
    • ここで書くテーブル名は、そのまま採用されるわけではなく、プリフィックスが付加される。なのでDB操作時のSQL文でも、プリフィックスの考慮を忘るべからず。
  • DB操作は、$xoopsDB っていう変数のメンバを使うべし。
  • $xoopsDB->queryF( $query ) は、UPDATE系でコールすべし。それ以外は $xoopsDB->query( $query ) を使うべきっぽい。

preload

  • XOOPS_ROOT 直下の preloadフォルダ と、モジュールの下の preloadフォルダ では動作が違う。全ページに影響させたい場合は XOOPS_ROOT 直下の preloadフォルダ に置くべきっぽい。(が、このあたりは全然把握不足)

REST

  • XMLとかJSONを返すようなwebサービスのページを作りたい場合、そのページの最後の「include XOOPS_ROOT_PATH."/footer.php”;」をコールせずに、自前でechoすれば良いっぽい。(が、お作法的に正しいのかわからない)

表示

条件によって左右のサイドバーとかを非表示にしたい。

  • 各領域の表示をON,OFF出来るような条件文を書いたテーマを自前で用意して対応する。(が、もっとスマートな方法があるのかもしれない。)

テンプレート

  • smartyの書式は「<{○○}>」で括るべし。

トラブったメモ

  • 日本語が表示されない。日本語のlanguageファイルが無視される。
    • UTF8 が標準文字コードっぽいので、languageファイル は 「japanese」ではなく「ja_utf8」になる。(本来は「english」も含めた3種を用意すべき?)
  • テンプレートファイルを編集してもページが変わらない。
    • テンプレートを書き換えたらモジュールのアップデートが必要。もっと手軽な方法があるなら知りたい。
  • モジュールのアンインストールしたとき、作ったテーブルを削除してくれない。
    • xoops_version.php の $modversion[’tables’][index] = “テーブル名” の記述が漏れてるか確認。

2014年7月6日(日曜日)

XOOPS Cube の進化っぷりにビックリ

カテゴリー: - takatsuka @ 09時30分36秒

新規で会員制サイトをこさえようと思っておったところ、昨今はフリーの CMS (とかその手のもの)が沢山あるので迷っておりました。

WordPress や OpenPNE とかは活発そうだし、Movable Type は有料版もあるだけに品質は良さそうだし。
どれであっても便利っぽいし、カスタマイズすれば本来の用途以外にも使えそうで、何を選択しても間違いではなさそう。

がしかし今回は、自社サイトでも使わせてもらってる XOOPS でやってみようかなと思いました。
諸々の事柄に対して一番ツブシが利きそう。(って抽象的な想像な上に、根拠もそういう印象持っただけって理由なんですが。)

なにはともあれ、自社サイトはもう10年近く前にこさえたものってこともあり、今の XOOPS Cube の進化っぷりに物凄く驚いた次第。

インストールが簡単なのはそのまま、プラグインとかテーマは一覧から選んでインストールできるようになってる、アップデートも通知してくれちゃって。昨今の CMS に全然見劣りしてない。
日本語との相性やプラグイン開発用の情報は問題なさそうだし。

色々な CMS を比較できるほど触ったわけではなく詳しくもないんですが、会員制サイトを、吊るしではない前提でこさえるなら、XOOPS CUBE 良さそうだなって思いました。開発者の方に感謝です。


2014年5月9日(金曜日)

template クラスの中の template の定義

カテゴリー: - takatsuka @ 23時36分03秒

C++ は使い続けてないと書式を忘れてしまいます。老化かな。
というわけで書き方を覚えていられず再び悩んでしまったので備忘録の為に。

template <typename TA> struct ClassA
{
    template <typename TB> struct ClassB
    {
        static const TB id;
        template <int TC> struct ClassC
        {
            static int Func();
        };
    };
};

template <typename TA> template<typename TB> const TB ClassA<TA>::ClassB<TB>::id = 999;

template <typename TA> template<typename TB> template<int TC> int ClassA<TA>::ClassB<TB>::ClassC<TC>::Func() {     return TC * TC; }

void Func() {     long i = ClassA<int>::ClassB<long>::id;     int n = ClassA<double>::ClassB<float>::ClassC<1>::Func(); }


2014年4月19日(土曜日)

VPS 上で仮想化を活かそうか

カテゴリー: - takatsuka @ 00時41分41秒

以前から試したいと思ってたことを隙間を見てやってみました。

流行りのVPSサービスを使って、その上で、さらに仮想化して複数のOSを動かすこと。Nested Virtualization って言葉もあるみたい。

具体的には さくらのVPS を利用して、まず winodws2003server std x86 を動かして、その上に VirtualBox で BlueOnyx5107R(CentOS6.5 x86) を動かしてみました。

・w2k3 は Virtio を有効にしてインストールしたかったのですが、w2k3 のインストーラは追加ドライバを FDD からしか読めないようで断念しました。Virtio 無効なら素の ISO イメージですんなりインストールできました。

・w2k3 の上に、VirtualBox ではなく VMware server 1.0 を入れてもみましたが、そのケースでは BlueOnyx5107R が起動できず断念しました。(なお 5106R は行けました。)

・暇があれば w2k3 ではなく linux 入れて KVM が動かくか試したいです。さくらのVPS 自体が KVM なので、KVM on KVM ってことになるんですかね。

・なお実運用となるとライセンス云々やVPS上の禁止事項を要確認かもですが、ここでは触れません。

・気になるパフォーマンスについてですが BlueOnyx のコンパネ操作は普通に使えました。・・だけじゃ参考にならないかもですがちょっとよくわかりません。というのも、さくらのVPS は本契約するまでは帯域制限がかかっているようで、回線周りが絡むものについては判断できません。しかし、PentiumM クラスの1台で数十のサイトを同居させてるくらいの物は現役で沢山稼働してますし、動画配信とかしなければ Nested Virtualization でも十分使える気がします。

・VirtualBox 上に windows2000 も入れてみたので、暇があれば何か派手なベンチソフトを動かして確認してみたいです。が、ベンチソフトって結果の数値をみてもどう判断していいかわからないので、ホントに暇だったらやるかもくらいで。。。

・・・という感じです。

ちなみに、調査するちょっと前に知ったんですが、VPSサービスって、ユーザーにはHDDイメージを提供してはくれないらしく、それが意外でした。つまり、仮想化のメリットである、環境丸ごとをHDDイメージファイルで扱えるってことが出来ないみたいです。(技術と時間があればなんとかなりそうですが、サービスとしてはどこの業者も扱ってないみたいです。たぶん。この認識に誤りあればぜひご指摘頂きたいです。)
さらに、VPSなんだからスペックアップのサービスくらいは提供されてるんだろうと思ってたら、たぶんこれも今現在どこも提供してくれてないみたいです。(多分です。これももし違ったらご指摘頂きたいです。)

このヘンはきっと色々と事情があるんでしょう。想像するに技術的な問題だけじゃなさそうだなって気がします。

というわけで、VPS 上でさらに仮想化をすることのメリットとしては、仮想化のホストを管理下に置けるので、HDD イメージのファイルコピーのみでサーバーを引っ越すとか、手元のローカル環境で起動してデバッグするとか、が可能になるというあたりでしょうか。

特にサーバーの引っ越し作業(=ほぼ同等の環境の最再構築作業)はホントに億劫です。手間もコストも忍耐力(ハナから仮想化しとけばこんなメンドウなことブツブツ・・って思わないようにする忍耐)も必要です。これを避けることが出来るだけでも十分メリットです。

というか、そんなことを考えるくらいなら、コストアップを覚悟して IaaS とかのクラウドサービス(って言うのでしょうか)を使えばいいってことなるのかもですが、そっちはまだ勉強不足です。頑張ります。

というわけで、単なる技術的な興味だけの調査ですが、いちおうIT業界の隅っこでカサカサと生息してる身分ですから、この手の雑多な知識や貧乏テクがお客様の無駄な出費を減らすことに繋がるだろう。って自分に言って自分をなだめてます。

金曜の夜のちびちび嗜みながらの作文はなかなかに筆(キータッチ)が進むということがわかりました。びばふらいどぇぇ。


2013年11月20日(水曜日)

boost::thread_specific_ptr

カテゴリー: - takatsuka @ 23時36分22秒
boost::thread_specific_ptr がとても便利なのでよく利用させてもらってます。ありがとうございます。
がしかし、boostのバージョンを最新にすると、実行時に reset で落ちるようになってしもた。
boost1.51はOK。1.52以降だと落ちるみたい。
単純なサンプルプログラム程度だと正常なんだけど、magome では落ちてしまう。
自分のスキルでは、boost の中のコードを追ったり、使い方の問題なのかとかの原因の切り分けが難しいので、とりあえず、boost1.51 を使い続けることにしようか・・とはいえ、今後新しい boost を使えないってのは、ちょっと残念だし・・・。
いっそのことまったく同じ機能のものをこさえちゃうか!?ビバ車輪の再発明!!
・・ウソデス。boost::thread_specific_ptr かその使い方を調べたほうがいいすね。スミマセン・・・。

2013年9月1日(日曜日)

MIDIシーケンサソフト「Magome」

カテゴリー: - takatsuka @ 22時45分37秒

ひっそりと粛々とサンデープログラミングで進めてた MIDIシーケンサソフトを公開してみようと思います。
まだまだ実験アプリの域を出ていませんが、公開することで開発に多少でも弾みがつくと嬉しいなと思っています。
音楽制作が好きな方や興味をお持ちの方のご意見頂けば幸いに思います。
https://tr9.sakura.ne.jp/magome/

  • 開発コードネームは「Magome」です。
  • クラウドベースの MIDIシーケンサソフトです。一通りの楽曲制作作業が出来ることを念頭に、フルスペックの DAW を目指しています。
  • 音楽制作をしている方及び興味を持っている方を対象に、スタンドアロンとしても使いやすい音楽制作環境ということと、ネットならではの面白い使い方の二つを提案していきたいと思っています。

よろしくお願い申し上げます。


2013年8月18日(日曜日)

ホームページの引っ越し

カテゴリー: - takatsuka @ 21時36分59秒
  • ホームページの引っ越し。 ホームページの引っ越しというかPHP&MySQLのバージョンアップをしてみました。 XOOPSを使わせて頂いておりますが、ちょっと一筋縄では行かないとこがあったので、そのへんの備忘録を兼ねて。
    1. DBのバックアップを取る。phpMyAdmin を使った。
    2. PHPを 5.2 → 5.4 に変更
    3. MySQL を 4.x → 5.5 に変更
    4. phpMyAdminで、DBを復元
      1. 新DB は全て ujis を選択して使う。
      2. 復元するときに logcounterx まわりでエラー。 → 原因はわからんが、アクセスのログなので重要ではなさげと思って復元をあきらめた。
      3. 復元するときに CREATE TABLE でエラー。TIMESTAMP(14) を TIMESTAMP に変更。
    5. XOOPSの設定ファイル(mainfile.php )を変更。DBのホスト名やDB名、パスワード等。
    6. 文字化け対策に、/class/database/mysqldatabase.php 236行目付近の$result =& mysql_query($sql, $this->conn); の上に mysql_query("SET CHARACTER SET ujis”, $this->conn); を追加
    7. PHP5.4で、htmlspecialchars のデフォルト引数が変更されたようなので、コード上の全ての htmlspecialchars の引数を明示的に指定するように変更。
      • 旧PHPと同じ動作をするデフォルト引数付きの関数を用意して関数をそっちに全置換(という手抜き対応)。
      • 注意点として、$var->htmlspecialchars みたいな箇所があるが、よくわからんので、ここは変更しない。
    8. tinyd のコンテンツの対応
      1. コンテンツファイルが EUC-JP になってないものが表示されなくなったので、EUC-JPで保存し直し。
      2. コンテンツファイルによっては文字が化けるみたいなので、頭のほうに <!– <p>ページ文字化けしないように</p> –> みたいな文を挿入。原因はよくわからんがこれで文字化けは消えた。
      3. ページラップ mod_rewrite が動作しなくなった。原因はよくわからんので、WRAP2 に変更してお茶を濁す。

2013年7月21日(日曜日)

リファラースパム

カテゴリー: - takatsuka @ 21時27分12秒

ログみると、リファラースパムで .ru ドメインがやたら多いので、そのあたりで流行ってんのかなと思ってたら、 .ws なんていうドメインも出てきた。よくわからんですがワークステーション的なそそるドメインだなと思いました。


2013年7月12日(金曜日)

PHP テンプレートライブラリ tinyTemplate (改)

カテゴリー: - takatsuka @ 23時39分53秒

PHP のテンプレートライブラリ tinyTemplate を使わせていただいております。
ファイル1つだし、手軽なのがいいです。ほんとにありがとうございます。
んがしかし、{if:$??} が複数ある場合に、二つ目以降が期待通りにパースされないようで、これがちょっと不便なのでそこだけ改造して使ってます。
些細な改造ですが、同様に不便に感じてる人もいるかもと思いました。

http://www.thinkridge.com/modules/mydownloads/

ファイル(クラス)1つそのままありますが、tinyTemplate からの変更箇所は function parse_if だけですので、この箇所だけコピペしても使えます。
参考になれば幸いです。


2013年7月7日(日曜日)

PHP の文字列に埋め込む

カテゴリー: - takatsuka @ 20時50分48秒
class Database
{
  const dbName = 'user';
  const column = 'id';

  public function __construct()   {     ・・・     $query = "CREATE TABLE {self::dbName} ({self::column} INT)";     ・・・   } };

PHP で、↑のように、文字列定義の中にオブジェクト定数を埋め込む記述が出来ないものか、けっこう調べてみたけど、今は出来ないっぽい。無念。

オブジェクト定数だけじゃなく、関数コールを埋め込みたいとか、ユーザーの要求があるんじゃなかろうかという気がするので、 PHP のバージョンアップでそのうち対応してくれることを期待しよう。と思った次第。


2013年6月15日(土曜日)

boost::asio ソケットを別プロセスに渡す

カテゴリー: - takatsuka @ 10時11分29秒

久しぶりに boost::asio ネタです。

サーバーで、acceptしたソケットを、別のプロセスに渡したい場合があると思います。
そのあたり boost::asio では簡単にできるんだろうか?と思い、ソースを眺めたり、Examples 見たりしたけど、そのものズバリっぽいものが見当たらない。
Examples には fork の項に近いのがあったんですが、fork() とか使ってるとこからして windows 向けではなさげだし・・・、

というわけで、今回のネタも windows 向けです。
サーバーを windows でってのがそもそも・・・という意見もありそうですが、windows でも P2P アプリを作りたいとか、ちょっとは需要あると思うんです。

というわけで、boost::asio で、accept したソケットを別のプロセスに渡す方法ですが、
結局、boost::asio だけで閉じることは出来ず win32 api を使ってます。

やることは、
複製元のソケットで、

boost::asio::ip::tcp::socket socket;
・・・
WSAPROTOCOL_INFO pi = {0,};
::WSADuplicateSocket( socket.native(), ::GetCurrentProcessId(), &pi);
SOCKET h = ::WSASocket( pi.iAddressFamily, pi.iSocketType,pi.iProtocol, &pi, 0, 0 );
socket.close();

ってハンドルをこさえて、複製先の子プロセスでは、

socket.assign( boost::asio::ip::tcp::v4(), h );

とやることで実現してます。

注意点としては、

#define BOOST_ASIO_DISABLE_IOCP 1

を入れることなんですが、これを入れることのデメリットがよくわからずにやってます。

こさえたサンプルアプリのソース一式を置いておきますので、必要な方は見てみてください。そしてダメ出しとかお願いします。
クライアント側がないと動作が見にくいという場合、boost::asio 使ったチャットソフトが同じところにありますので、そちらも参照下さい。

http://www.thinkridge.com/modules/mydownloads/

そういえば、いつのまにか boostpro サイト が終了してたんですね。
インストーラーは便利だっただけにとても残念です。
運営の方、お疲れ様でござました。感謝です。


2013年6月2日(日曜日)

納品

カテゴリー: - takatsuka @ 20時59分08秒

お取引先で社内サーバー機のハードのトラブルをきっかけに、古くなったサーバー機のリプレースということで対応しました。

いわゆるクラウド化はせずに、社内の閉じたネット内のサーバーとして使うことが望まれていたのですが、
仮想化はしておいたほうが良いと考えて、旧サーバーの機能を仮想PCとして構築し直して、対応しました。

もちろん、貧乏テクノウハウを駆使して、必要最小限のサーバー機と業務でも使える安いソフトや無料ソフトを駆使して対応。

仮想PCとして事前に環境を構築しておいて、納入日に機材ごと持参して交換作業に臨んだんですが、
旧サーバーの使われ方の細かなとこはその場で対応。けっこう手こずりました。

しかし今回ここで仮想化しておいたので、もし今後、またサーバー機が壊れたとか、クラウドにしたい、とかいう状況になっても対応は簡単なハズ。

しかし、行き帰りの運転時間は往復6時間半。
いつも思うけど千葉県の特に船橋の道路事情はほんとにひどい。
ふなっしーは梨のアピールではなく、道路事情改善のアピールをしてほしいと思いました。


2013年5月12日(日曜日)

WebKit (windows版) のビルド

カテゴリー: - takatsuka @ 21時42分52秒

WebKit (windows版) のビルド

けっこう苦労したので備忘録として。

開発環境の準備
・VisualStudio 2005 standard をインストール。

・Microsoft Platform SDK for Windows Server 2003 R2 をインストール

・Microsoft DirectX SDK (February 2010) をインストール

・QuickTime SDK をインストール

・VisualStudio2005 のサービスパックとか WindowsUpdate で出てくる VisualStudio 系のパッチを、全てインストール

・Cygwin をインストール。( webkit.org にある cygwin-downloader.zip )

・VisualStudio2005 の メインメニュー [ツール] - [オプション] - VC++ ディレクトリ で、以下を追加
 実行可能ファイル
  C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Bin
 インクルードファイル
  C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Include
  C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Include/mfc
  C:/Program Files/Microsoft DirectX SDK (February 2010)/Include
  C:/Program Files/QuickTime SDK/CIncludes
 ライブラリファイル
  C:/Program Files/Microsoft Platform SDK for Windows Server 2003 R2/Lib
  C:/Program Files/Microsoft DirectX SDK (February 2010)/Lib/x86
  C:/Program Files/QuickTime SDK/Libraries

ソ−スの用意

・WebKit Open Source Project の WebKit Nightly Builds から、ソースを取得 ( 自分の場合は WebKit-r149799 )

・C:/cygwin/home/(user)/WebKit に展開。

・WebKitSupportLibrary.zip をダウンロードして、zipファイルのまま上記のフォルダに配置。

・cygwinを起動。

・$ WebKit/Tools/Scripts/update-webkit を実行。成功を確認する。

ビルド

・$ WebKit/Tools/Scripts/build-webkit –debug を実行。

・或いは、C:/cygwin/home/(user)/WebKit/Source/WebKit/win/WebKit.vcproj/WebKit.sln を vs2005 で開き、ビルド。

ビルドエラーを取る

・C:/cygwin/home/(user)/WebKit/Source/WTF/WTF.vcproj/copy-files.cmd の
# FIXME: Why is WTF copying over create_hash_table? の行をコメントアウト。

・warning C4819:ファイルは、現在のコード ページ・・・と出るファイルを全て、unicode 形式で保存しなおす。

実行

・Windows 用 Safari 5.1.7 をインストール。

・WebKit.sln を vs2005 で開き、WebKitLib をスタートアッププロジェクトにする。

・WebKitLib のプロパティページ - デバッグ で以下に設定
 コマンド $(PROGRAMFILES)/Safari/Safari.exe
 環境 WEBKITNIGHTLY=$(OutDir)

・WebKitLib を実行。
 ビルドしたDLLを利用する形で safari が起動する。
 がしかし、実行したとたんに、実行時エラーで終了。とりあえずここまでで週末終了。

注意事項

・WebKit Nightly Builds から古いものが取得出来るが、WebKitSupportLibrary.zip とのバージョン不整合が起こるのか、update-webkit が失敗する。解決策不明。

・DirectX SDK は、VisualStudio2005 で使える版を使う。

・プロジェクト : warning PRJ0018 : 以下の環境変数が見つかりませんでした: $(PRODUCTION) とか出ることがあるが無視してOKらしいので無駄に悩まないこと。

以上、webkit の関係者に感謝しつつ、どなたかの参考になれば幸いです。


2013年5月7日(火曜日)

了解です

カテゴリー: - takatsuka @ 12時30分49秒

「了解です」を、取引先とか目上の人とかに使うのは正しくない。ということは薄々聞いていましたが、正しくは「承知しました」とか「承りました」というのが、どうにも腑に落ちない気がするのはどうしてだろうか。

たぶん自分の業務的に、取引先も目上の人も携わってるプロジェクトのメンツという感覚だからなのかもしれないです。
イメージとしては、ホワイトベースの乗組員的な感じ(?)。

ブライト「アムロ!ガンダムを発進させろ!」
アムロ「承知しました!アムロ発進します!」

やっぱりヘンです。アムロには「了解です!」と言ってほしい。

時代劇なら、
アムロ「承知っ!」
でもアリだけど。

(自分がアムロだと言ってるわけではないですし、リュウとかハヤトであっても同じです。)

というわけで「了解です」の代わりの、しっくりくる言葉を御存じの方おられたら教えてほしいです。

そういえば、「御苦労様」も、言える相手は目下(か同僚くらい)の人のみで、目上の人には「お疲れ様」です。
とのことですが、これもイマイチしっくりこないです。
しっくりこないどころか、自分の感覚的には逆な気がしてます。
宅配のドライバーさんには、敬意を払って「御苦労様です」と言いたくなるし。
なんでだろうか・・。

きっと、任侠映画等で度々登場する「お勤め御苦労様です!」というセリフが刷り込まれているんじゃなかろうかと思ってます。

というわけで、ビジネスマナーとか正しい日本語とかあるとは思いますが、
風習というか世俗というか地域や世代の文化もありますし、そういうとこを尊重して頂くか、お手柔らかに願い度、と思う次第です。


67 queries. 0.153 sec.
Powered by WordPress Module based on WordPress ME & WordPress

カレンダー
2019年 3月
« 11月    
 12
3456789
10111213141516
17181920212223
24252627282930
31  
月別過去ログ
カテゴリ一覧
検索
最近の投稿
最近のコメント
投稿者ブロック