BIO_s_accept can be used as an server socket object, and has it’s own data struct BIO_ACCEPT, which is stored in BIO.ptr. in the struct, a status value BIO_ACCEPT.state is introduced to record the current state of socket, The statues are:
#define ACPT_S_BEFORE 1
#define ACPT_S_GET_ACCEPT_SOCKET 2
#define ACPT_S_OK 3
The status indicate what to do next step. Use the internal function conn_state() take the next action. Until call the blocking accept() or an error happens.
The BIO_s_connect’s bio_accept_st uses a bio list to restores the accepted socket, and add the remote socket at the end of the list.
If the remote client is closed, the BIO_read() will wake and return 0; if the close is not graceful, will return -1;
Notes that all the socket functions run in blocking mode.
All the functions are implemented in file Bss_acpt.c.
Use steps:
Create a socket bio and input port number.
You may use just one line:
BIO *abio = BIO_new_accept("2000");
Then accept:
If (BIO_do_accept(abio) <= 0) …
Notes the function only initial the BIO, if wanna accept connection you need to call it twice. The first time push state from ACPT_S_BEFORE to ACPT_S_GET_ACCEPT_SOCKET, the second time do the actual accept BIO_accept().
Get remote IP
So strange, BIO_s_accept doesn’t support get the remove IP. Only can get the port number… while the BIO popped don’t have a BIO.ptr, and can only be used to send and recv data.
/* BIO_s_accept_socket() */
#define BIO_set_accept_port(b,name) BIO_ctrl(b,BIO_C_SET_ACCEPT,0,(char *)name)
#define BIO_get_accept_port(b) BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,0)
/* #define BIO_set_nbio(b,n) BIO_ctrl(b,BIO_C_SET_NBIO,(n),NULL) */
#define BIO_set_nbio_accept(b,n) BIO_ctrl(b,BIO_C_SET_ACCEPT,1,(n)?"a":NULL)
#define BIO_set_accept_bios(b,bio) BIO_ctrl(b,BIO_C_SET_ACCEPT,2,(char *)bio)
Our solution: to get remote IP:
char* pch = NULL;
pch = ((BIO_ACCEPT*)(abio->ptr))->addr;
and you have to copy definition of BIO_ACCEPT ahead of code, and BIO_ACCEPT.addr only restore the recent connected client IP.
And also you may get the remote IP by the classical way:
SOCKET sClient = (SOCKET)BIO_get_fd(cbio, NULL);
SOCKADDR sName;
int iAddLen = sizeof(sName);
getpeername(sClient, &sName, &iAddLen);
OK, said so much, now give the sample echo server code:
DWORD WINAPI listenThread(LPVOID lpParameter) { BIO *abio, *cbio; HANDLE hThread; unsigned long ulthID = 0;
if (NULL == (abio = BIO_new_accept("2000"))) return 0;
if(BIO_do_accept(abio) <= 0) { printf("Error setting up accept/n"); return 0; } printf("Server started/n");
while(TRUE) {
if(BIO_do_accept(abio) <= 0) { printf("Error setting up accept/n"); return 0; }
// try to get client IP char* pch = NULL; pch = ((BIO_ACCEPT*)(abio->ptr))->addr; //pch = BIO_get_accept_port(abio); printf("client IP:%s/n", pch);
cbio = BIO_pop(abio); //SOCKET sClient = (SOCKET)BIO_get_fd(cbio, NULL); //SOCKADDR sName; //int iAddLen = sizeof(sName); //getpeername(sClient, &sName, &iAddLen); //pch = inet_ntoa(*(in_addr*)(sName.sa_data +2)); //printf("client IP:%s/n", pch);
hThread = CreateThread(NULL, 0, rwThread, cbio, NULL, &ulthID); if (hThread == NULL) { printf("CreateThread failed./n" ); return 0; } printf("client thread %x created/n", ulthID); } }
DWORD WINAPI listenThreadBss(LPVOID lpParameter) { BIO *bClient=NULL,*bAccept=NULL; int sock,ret; char *addr=NULL; HANDLE hThread; unsigned long ulthID = 0;
if (INVALID_SOCKET == (sock = BIO_get_accept_socket("2000", BIO_BIND_NORMAL))) return 0; printf("Server started/n");
while(TRUE) {
// the same as accept, blocking mode, with the address containing remote client address. // but if addr == NULL, memory would be allocated. // the ret is the SOCKET object. if (INVALID_SOCKET == (ret = BIO_accept(sock,&addr))) return 0; printf("client IP:%s/n", addr);
bClient = BIO_new(BIO_s_socket()); if (NULL == bClient) return 0; // set the SOCKET object "ret" into bio.num. then the bClient can be used to read and write from remote client. BIO_set_fd(bClient,ret,BIO_NOCLOSE);
hThread = CreateThread(NULL, 0, rwThread, bClient, NULL, &ulthID); if (hThread == NULL) { printf("CreateThread failed./n" ); } printf("client thread %x created/n", ulthID); } }
DWORD WINAPI rwThread(LPVOID lpParameter) { BIO *cbio = (BIO *)lpParameter; int sock,ret,len; char *addr=NULL; char in[80]; unsigned long ulthID = GetCurrentThreadId();
while(1) { memset(in,0,80); len=BIO_read(cbio,in,80); if(in[0]=='q' || -1 == len) break; printf("thread %x from the client: %s/n",ulthID, in);
// echo the message len = BIO_write(cbio, in, len); if (0 == len) break; } BIO_free(cbio); return 0; }
int _tmain(int argc, _TCHAR* argv[]) { HANDLE hThread;
hThread = CreateThread(NULL, 0, listenThread, NULL, NULL, NULL); //hThread = CreateThread(NULL, 0, listenThreadBss, NULL, NULL, NULL); if (hThread == NULL) { printf("CreateThread failed./n" ); }
WaitForSingleObject(hThread, INFINITE);
return 0; } |