となります。//--------------------------------------------------------------------- class VTcpNetworkClient { private: static int32 vtcp_rec_thread(void *Dummy); public: VTcpNetworkClient(BLooper *Owner); ~VTcpNetworkClient(void); int sock_connect(char *host,int port); int sock_send(void *data,int32 length); void sock_closed(void); int sock_accept(int s); }; //---------------------------------------------------------------------
/**** ファイル名 : MainWindow.h ****/
#ifndef VTCPNETH #define VTCPNETH //--------------------------------------------------------------------- #include <Be.h> #include <be/net/socket.h> //--------------------------------------------------------------------- #define MSG_VTCP_CLOSED 'clsd' #define MSG_VTCP_RECV 'recv' //--------------------------------------------------------------------- class VTcpNetworkClient { private: struct RecThreadData { int sock; BLooper *owner; }; int nsock; int32 recthread; char threadname[32]; BLooper *MyOwner; static int32 vtcp_rec_thread(void *Dummy); bool MakeThreadName(char *name,char *des); public: VTcpNetworkClient(BLooper *Owner); ~VTcpNetworkClient(void); //----------------------------------------------------------------- int sock_connect(char *host,int port); int sock_send(void *data,int32 length); void sock_closed(void); int sock_accept(int s); int GetSocket(void) { return nsock; }; }; //--------------------------------------------------------------------- #endif
//--------------------------------------------------------------------- VTcpNetworkClient::VTcpNetworkClient(BLooper *Owner) { MyOwner=Owner; recthread=-1; nsock=-1; threadname[0]='\0'; } //---------------------------------------------------------------------
sock_connect関数では、ソケットの作成からサーバへの接続、受信スレッドの作成まで行います。//--------------------------------------------------------------------- VTcpNetworkClient::~VTcpNetworkClient(void) { sock_closed(); } //---------------------------------------------------------------------
同名のスレッドが存在するかを確認するにはfind_thread関数を使用します。スレッドが存在しなければ、find_thread関数はB_NAME_NOT_FOUNDを返します。//--------------------------------------------------------------------- bool VTcpNetworkClient::MakeThreadName(char *name,char *des) { char wk[33]; static int i=0; for(;i<1024;i++) { sprintf(wk,"%s%d",name,i); if(find_thread(wk)==B_NAME_NOT_FOUND) { strcpy(des,wk); return true; } } return false; } //---------------------------------------------------------------------
//--------------------------------------------------------------------- int VTcpNetworkClient::sock_connect(char *host,int port) { struct sockaddr_in sa; int r; nsock=socket(AF_INET, SOCK_STREAM, 0); sa.sin_family=AF_INET; sa.sin_port=htons((short)port); sa.sin_addr.s_addr=inet_addr(host); if(sa.sin_addr.s_addr==0xFFFFFFFFul) { hostent *h; h=gethostbyname(host); if(h!=NULL) sa.sin_addr.s_addr=*(unsigned int *)h->h_addr; } memset(sa.sin_zero,0,sizeof(sa.sin_zero)); r=connect(nsock,(struct sockaddr *)&sa,sizeof(sa)); if(r!=-1) { struct RecThreadData rdata; rdata.sock=nsock; rdata.owner=MyOwner; if(MakeThreadName("ClientRecvThread",threadname)) { recthread=spawn_thread(vtcp_rec_thread,threadname, B_NORMAL_PRIORITY,NULL); send_data(recthread,63,(void *)&rdata,sizeof(rdata)); resume_thread(recthread); } else { closesocket(nsock); nsock=-1; } } else { closesocket(nsock); nsock=-1; } return nsock; } //---------------------------------------------------------------------
繰り返して使用することを考慮して、nsockとthreadnameを初期化するのを忘れないでください。//--------------------------------------------------------------------- void VTcpNetworkClient::sock_closed(void) { int32 fthread; if(nsock!=-1) { closesocket(nsock); nsock=-1; } if(strlen(threadname)>0) if((fthread=find_thread(threadname))!=B_NAME_NOT_FOUND) { kill_thread(fthread); threadname[0]='\0'; } } //---------------------------------------------------------------------
//--------------------------------------------------------------------- int VTcpNetworkClient::sock_send(void *data,int32 length) { int r; r=send(nsock,data,length,0); if(r==-1) { sock_closed(); BMessage *sendmsg=new BMessage(MSG_VTCP_CLOSED); sendmsg->AddInt32("socket",nsock); MyOwner->PostMessage(sendmsg); delete sendmsg; } return r; } //---------------------------------------------------------------------
//--------------------------------------------------------------------- int32 VTcpNetworkClient::vtcp_rec_thread(void *Dummy) { int sock; BLooper *Owner; struct timeval tv; struct fd_set fds; BMessage *sendmsg; unsigned char buf[1024]; ssize_t rec; RecThreadData rdata; int32 sender; int32 code; code=receive_data((thread_id *)&sender,(void *)&rdata,sizeof(rdata)); Owner=rdata.owner; sock=rdata.sock; do{ do{ tv.tv_sec=2; tv.tv_usec=0; FD_ZERO(&fds); FD_SET(sock,&fds); select(32,&fds,NULL,NULL,&tv); } while(FD_ISSET(sock,&fds)==0); rec=recv(sock,buf,sizeof(buf),0); if(rec<=0) { sendmsg=new BMessage(MSG_VTCP_CLOSED); sendmsg->AddInt32("socket",sock); Owner->PostMessage(sendmsg); delete sendmsg; } else { sendmsg=new BMessage(MSG_VTCP_RECV); sendmsg->AddInt32("socket",sock); sendmsg->AddData("data",B_ANY_TYPE,(void *)buf,rec); Owner->PostMessage(sendmsg); delete sendmsg; } } while(rec>0); return rec; } //---------------------------------------------------------------------
//--------------------------------------------------------------------- int VTcpNetworkClient::sock_accept(int s) { struct sockaddr_in sa; int sa_size; sa.sin_family=AF_INET; memset(sa.sin_zero,0,sizeof(sa.sin_zero)); sa_size=sizeof(sa); nsock=accept(s,(struct sockaddr *)&sa, &sa_size); if(nsock!=-1) { struct RecThreadData rdata; rdata.sock=nsock; rdata.owner=MyOwner; if(MakeThreadName("ServerRecvThread",threadname)) { recthread=spawn_thread(vtcp_rec_thread,threadname, B_NORMAL_PRIORITY,NULL); send_data(recthread,63,(void *)&rdata,sizeof(rdata)); resume_thread(recthread); } else { closesocket(nsock); nsock=-1; return -1; } } return nsock; } //---------------------------------------------------------------------
/**** ファイル名 : MainWindow.h ****/
#ifndef MAINWINDOW #define MAINWINDOW //--------------------------------------------------------------------- #include <Be.h> #include <be/net/socket.h> #include "vtcpnet.h" //--------------------------------------------------------------------- #define MAINWINDOW_TITLE "Network" #define MAINWINDOW_POSITION_LEFT 100 #define MAINWINDOW_POSITION_TOP 100 #define MAINWINDOW_POSITION_WIDTH 400 #define MAINWINDOW_POSITION_HEIGHT 128 #define MAINWINDOW_WINDOWSTYLE (B_TITLED_WINDOW) //--------------------------------------------------------------------- #define MSG_CONNECT 'cnnb' #define MSG_LISTEN 'lstb' #define MSG_SEND 'sndb' //--------------------------------------------------------------------- class BAppMainView : public BView { public: BStringView *strview; BTextControl *textctrl; BButton *connectbtn; BButton *listenbtn; BButton *sendbtn; BAppMainView(BRect frame); BCheckBox *portsw; }; //--------------------------------------------------------------------- class BAppMainWindow : public BWindow { private: int nsock; VTcpNetworkClient *vtcp; public: BAppMainView *mainview; //----------------------------------------------------------------- BAppMainWindow(BRect frame,const char *title); virtual void MessageReceived(BMessage *msg); //----------------------------------------------------------------- bool QuitRequested(); //----------------------------------------------------------------- void sock_connect(void); void sock_listen(void); void sock_send(void); }; //--------------------------------------------------------------------- #endif
/**** ファイル名 : MainWindow.cpp ****/
//--------------------------------------------------------------------- #include "MainWindow.h" //--------------------------------------------------------------------- BAppMainWindow::BAppMainWindow(BRect frame,const char *title) :BWindow(frame,title,MAINWINDOW_WINDOWSTYLE,0) { mainview=new BAppMainView(Bounds()); AddChild(mainview); nsock=-1; vtcp=NULL; } //--------------------------------------------------------------------- void BAppMainWindow::MessageReceived(BMessage *msg) { switch(msg->what) { case MSG_SEND: sock_send(); break; case MSG_CONNECT: sock_connect(); break; case MSG_LISTEN: sock_listen(); break; case MSG_VTCP_CLOSED: vtcp->sock_closed(); mainview->strview->SetText("Disconnect"); break; case MSG_VTCP_RECV: { char *buf; ssize_t leng; msg->FindData("data",B_ANY_TYPE,(const void **)&buf,&leng); mainview->strview->SetText(buf); } break; default: BWindow::MessageReceived(msg); } } //--------------------------------------------------------------------- BAppMainView::BAppMainView(BRect frame) :BView(frame,"bappmainview",B_FOLLOW_ALL,B_WILL_DRAW) { BRect viewrect(Bounds()); connectbtn=new BButton(BRect(viewrect.right-144,8, viewrect.right-80,28),"connectbtn", "Connect",new BMessage(MSG_CONNECT), B_FOLLOW_RIGHT | B_FOLLOW_TOP); AddChild(connectbtn); listenbtn=new BButton(BRect(viewrect.right-72,8, viewrect.right-8,28),"listenbtn", "Listen",new BMessage(MSG_LISTEN), B_FOLLOW_RIGHT | B_FOLLOW_TOP); AddChild(listenbtn); sendbtn=new BButton(BRect(viewrect.right-72,98, viewrect.right-8,118),"sendbtn", "Send",new BMessage(MSG_SEND), B_FOLLOW_RIGHT | B_FOLLOW_TOP); AddChild(sendbtn); textctrl=new BTextControl(BRect(8,38,viewrect.right-8,58),"textctrl", NULL,"Default",NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); AddChild(textctrl); strview=new BStringView(BRect(8,68,viewrect.right-8,88),"strview", "NoData", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); AddChild(strview); portsw=new BCheckBox(BRect(8,98,40,118),"portsw","On:A,Off:B",NULL); AddChild(portsw); textctrl->SetEnabled(false); sendbtn->SetEnabled(false); } //--------------------------------------------------------------------- void BAppMainWindow::sock_connect(void) { mainview->connectbtn->SetEnabled(false); mainview->listenbtn->SetEnabled(false); vtcp=new VTcpNetworkClient(this); nsock=vtcp->sock_connect("127.0.0.1", 10000+((mainview->portsw->Value()==B_CONTROL_ON)?1:0)); if(nsock!=-1) { mainview->strview->SetText("Connect"); mainview->textctrl->SetEnabled(true); mainview->sendbtn->SetEnabled(true); } } //--------------------------------------------------------------------- void BAppMainWindow::sock_listen(void) { int ssock; struct sockaddr_in sa; int sa_size; mainview->connectbtn->SetEnabled(false); mainview->listenbtn->SetEnabled(false); ssock=socket(AF_INET,SOCK_STREAM,0); sa.sin_family=AF_INET; sa.sin_port=htons( 10000+((mainview->portsw->Value()==B_CONTROL_ON)?1:0)); sa.sin_addr.s_addr=INADDR_ANY; memset(sa.sin_zero,0,sizeof(sa.sin_zero)); bind(ssock,(struct sockaddr *)&sa,sizeof(sa)); listen(ssock,5); sa_size=sizeof(sa); vtcp=new VTcpNetworkClient(this); nsock=vtcp->sock_accept(ssock); closesocket(ssock); if(nsock!=-1) { mainview->strview->SetText("Connect"); mainview->textctrl->SetEnabled(true); mainview->sendbtn->SetEnabled(true); } } //--------------------------------------------------------------------- void BAppMainWindow::sock_send(void) { int r; r=vtcp->sock_send((void *)mainview->textctrl->Text(), (int32)strlen(mainview->textctrl->Text())+1); if(r==-1) { nsock=-1; mainview->strview->SetText("Disconnect"); } } //--------------------------------------------------------------------- bool BAppMainWindow::QuitRequested() { if(vtcp!=NULL) vtcp->sock_closed(); be_app->PostMessage(B_QUIT_REQUESTED); return true; }; //---------------------------------------------------------------------
と、「On:A,Off:B」と表記したチェックボックスを作成します。このチェックボックスがOnの場合はポートは10001を、Offの場合は10000を使用するようにします。portsw=new BCheckBox(BRect(8,98,40,118),"portsw","On:A,Off:B",NULL); AddChild(portsw);
まずは接続ですが、//--------------------------------------------------------------------- void BAppMainWindow::sock_connect(void) { mainview->connectbtn->SetEnabled(false); mainview->listenbtn->SetEnabled(false); vtcp=new VTcpNetworkClient(this); nsock=vtcp->sock_connect("127.0.0.1", 10000+((mainview->portsw->Value()==B_CONTROL_ON)?1:0)); if(nsock!=-1) { mainview->strview->SetText("Connect"); mainview->textctrl->SetEnabled(true); mainview->sendbtn->SetEnabled(true); } } //---------------------------------------------------------------------
でVTcpNetworkClientクラスのインスタントを作成し、vtcp=new VTcpNetworkClient(this);
で接続先のアドレスとポートを指定して、sock_connect関数を実行します。戻り値はconnect関数の戻り値と同じですので、失敗した場合は-1となります。nsock=vtcp->sock_connect("127.0.0.1", 10000+((mainview->portsw->Value()==B_CONTROL_ON)?1:0));
送信は、VTcpNetworkClientクラスのsock_send関数に送信するデータのアドレスとサイズを指定して実行するだけです。送信に失敗した場合は、ソケットをクローズしますので、MSG_VTCP_CLOSEDメッセージが通知されます。//--------------------------------------------------------------------- void BAppMainWindow::sock_send(void) { int r; r=vtcp->sock_send((void *)mainview->textctrl->Text(), (int32)strlen(mainview->textctrl->Text())+1); if(r==-1) { nsock=-1; mainview->strview->SetText("Disconnect"); } } //---------------------------------------------------------------------
受信は、MSG_VTCP_RECVメッセージに受信データが付加されたかたちで通知されますので、FindData関数でデータを取得するだけです。//--------------------------------------------------------------------- void BAppMainWindow::MessageReceived(BMessage *msg) { switch(msg->what) { case MSG_SEND: sock_send(); break; case MSG_CONNECT: sock_connect(); break; case MSG_LISTEN: sock_listen(); break; case MSG_VTCP_CLOSED: vtcp->sock_closed(); mainview->strview->SetText("Disconnect"); break; case MSG_VTCP_RECV: { char *buf; ssize_t leng; msg->FindData("data",B_ANY_TYPE,(const void **)&buf,&leng); mainview->strview->SetText(buf); } break; default: BWindow::MessageReceived(msg); } } //---------------------------------------------------------------------
最後にアクセプト(サーバ側処理)ですが、アクセプトするためには、bind関数でソケットの準備をして、listen関数でクライアントからの接続を待つ必要があります。VTcpNetworkClientクラスはクライアントからの受信待ちはサポートしていないため、この部分は前回と同様に作成する必要があります。//--------------------------------------------------------------------- void BAppMainWindow::sock_listen(void) { int ssock; struct sockaddr_in sa; int sa_size; mainview->connectbtn->SetEnabled(false); mainview->listenbtn->SetEnabled(false); ssock=socket(AF_INET,SOCK_STREAM,0); sa.sin_family=AF_INET; sa.sin_port=htons( 10000+((mainview->portsw->Value()==B_CONTROL_ON)?1:0)); sa.sin_addr.s_addr=INADDR_ANY; memset(sa.sin_zero,0,sizeof(sa.sin_zero)); bind(ssock,(struct sockaddr *)&sa,sizeof(sa)); listen(ssock,5); sa_size=sizeof(sa); vtcp=new VTcpNetworkClient(this); nsock=vtcp->sock_accept(ssock); closesocket(ssock); if(nsock!=-1) { mainview->strview->SetText("Connect"); mainview->textctrl->SetEnabled(true); mainview->sendbtn->SetEnabled(true); } } //---------------------------------------------------------------------
圧縮ファイル R5 Intel環境で確認 |
Be9thApp20010214.zip |
ソースファイル | BaseApp.h |
main.cpp | |
MainWindow.cpp | |
MainWindow.h | |
vtcpnet.cpp | |
vtcpnet.h |