//---------------------------------------------------------------------
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';
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
VTcpNetworkClient::~VTcpNetworkClient(void)
{
sock_closed();
}
//---------------------------------------------------------------------
sock_connect関数では、ソケットの作成からサーバへの接続、受信スレッドの作成まで行います。
//---------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------
同名のスレッドが存在するかを確認するにはfind_thread関数を使用します。スレッドが存在しなければ、find_thread関数はB_NAME_NOT_FOUNDを返します。
//---------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------
//---------------------------------------------------------------------
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';
}
}
//---------------------------------------------------------------------
繰り返して使用することを考慮して、nsockとthreadnameを初期化するのを忘れないでください。
//---------------------------------------------------------------------
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);
nsock=vtcp->sock_connect("127.0.0.1",
10000+((mainview->portsw->Value()==B_CONTROL_ON)?1:0));
で接続先のアドレスとポートを指定して、sock_connect関数を実行します。戻り値はconnect関数の戻り値と同じですので、失敗した場合は-1となります。
//---------------------------------------------------------------------
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");
}
}
//---------------------------------------------------------------------
送信は、VTcpNetworkClientクラスのsock_send関数に送信するデータのアドレスとサイズを指定して実行するだけです。送信に失敗した場合は、ソケットをクローズしますので、MSG_VTCP_CLOSEDメッセージが通知されます。
//---------------------------------------------------------------------
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);
}
}
//---------------------------------------------------------------------
受信は、MSG_VTCP_RECVメッセージに受信データが付加されたかたちで通知されますので、FindData関数でデータを取得するだけです。
//---------------------------------------------------------------------
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);
}
}
//---------------------------------------------------------------------
最後にアクセプト(サーバ側処理)ですが、アクセプトするためには、bind関数でソケットの準備をして、listen関数でクライアントからの接続を待つ必要があります。VTcpNetworkClientクラスはクライアントからの受信待ちはサポートしていないため、この部分は前回と同様に作成する必要があります。| 圧縮ファイル R5 Intel環境で確認 |
Be9thApp20010214.zip |
| ソースファイル | BaseApp.h |
| main.cpp | |
| MainWindow.cpp | |
| MainWindow.h | |
| vtcpnet.cpp | |
| vtcpnet.h |