* NEW: ServerSocket.Interface is a new property to define the interface the 
  socket will listen to. It uses a Linux-specific socket option, and I'm
  not sure that it really works.


git-svn-id: svn://localhost/gambas/trunk@3071 867c0c6c-44f3-4631-809d-bfa615b0a4ec
This commit is contained in:
Benoît Minisini 2010-07-26 16:18:51 +00:00
parent dbf5d2237a
commit 08d3f92581
5 changed files with 262 additions and 262 deletions

View file

@ -144,6 +144,7 @@ GB_DESC CNetDesc[] =
GB_CONSTANT("HostNotFound", "i", -6),
GB_CONSTANT("CannotBindSocket", "i", -10),
GB_CONSTANT("CannotListen", "i", -14),
GB_CONSTANT("CannotBindInterface", "i", -15),
/* SeverSocket, type */
GB_CONSTANT("Internet", "i", 1),
GB_CONSTANT("Local", "i", 0),

View file

@ -22,7 +22,6 @@
#define __CSERVERSOCKET_C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -32,6 +31,8 @@
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <net/if.h>
#include <errno.h>
#include "main.h"
#include "tools.h"
@ -39,17 +40,177 @@
#include "CServerSocket.h"
#include "CSocket.h"
DECLARE_EVENT(EVENT_Connection);
DECLARE_EVENT(EVENT_Error);
void srvsock_post_error(CSERVERSOCKET *_object)
static void srvsock_post_error(CSERVERSOCKET *_object)
{
GB.Raise(THIS, EVENT_Error, 0);
GB.Unref(POINTER(&_object));
}
static void CServerSocket_CallBack(int fd, int type, intptr_t lParam)
{
int okval=0;
char *rem_ip_buf;
unsigned int ClientLen;
CSERVERSOCKET *_object = (CSERVERSOCKET*)lParam;
if ( SOCKET->status != 1) return;
SOCKET->status=2;
ClientLen=sizeof(struct sockaddr_in);
THIS->Client=accept(SOCKET->socket,(struct sockaddr*)&THIS->so_client.in,&ClientLen);
if (THIS->Client == -1)
{
close(THIS->Client);
SOCKET->status=1;
return;
}
rem_ip_buf=inet_ntoa(THIS->so_client.in.sin_addr);
if ( (!THIS->iMaxConn) || (THIS->iCurConn < THIS->iMaxConn) ) okval=1;
if ( (!THIS->iPause) && (okval) )
GB.Raise(THIS,EVENT_Connection,1,GB_T_STRING,rem_ip_buf,0);
if ( SOCKET->status == 2) close(THIS->Client);
SOCKET->status=1;
}
static void CServerSocket_CallBackUnix(int fd, int type, intptr_t lParam)
{
//int position=0;
int okval=0;
unsigned int ClientLen;
CSERVERSOCKET *_object = (CSERVERSOCKET*)lParam;
if ( SOCKET->status != 1) return;
SOCKET->status=2;
ClientLen=sizeof(struct sockaddr_un);
THIS->Client=accept(SOCKET->socket,(struct sockaddr*)&THIS->so_client.un,&ClientLen);
if (THIS->Client == -1)
{
close(THIS->Client);
SOCKET->status=1;
return;
}
if ( (!THIS->iMaxConn) || (THIS->iCurConn < THIS->iMaxConn) ) okval=1;
if ( (!THIS->iPause) && (okval) )
GB.Raise(THIS,EVENT_Connection,1,GB_T_STRING,NULL,0);
if ( SOCKET->status == 2) close(THIS->Client);
SOCKET->status=1;
}
/*********************************************************
Starts listening (TCP/UDP/UNIX)
**********************************************************/
static int do_srvsock_listen(CSERVERSOCKET* _object,int mymax)
{
int NoBlock=1;
int retval;
int auth = 1;
if ( (!THIS->iPort) && (THIS->type) ) return 8;
if ( SOCKET->status >0 ) return 1;
if (mymax<0) return 13;
if ( (!THIS->type) && (!THIS->sPath) ) return 7;
if (THIS->type)
{
THIS->so_server.in.sin_family=AF_INET;
THIS->so_server.in.sin_addr.s_addr=INADDR_ANY;
THIS->so_server.in.sin_port=htons(THIS->iPort);
SOCKET->socket=socket(PF_INET,SOCK_STREAM,0);
}
else
{
unlink(THIS->sPath);
THIS->so_server.un.sun_family=AF_UNIX;
strcpy(THIS->so_server.un.sun_path,THIS->sPath);
SOCKET->socket=socket(AF_UNIX,SOCK_STREAM,0);
}
if ( SOCKET->socket==-1 )
{
SOCKET->status=-2;
GB.Ref(THIS);
GB.Post(srvsock_post_error,(intptr_t)THIS);
return 2;
}
// thanks to Benoit : this option allows non-root users to reuse the
// port after closing it and reopening it in a short interval of time.
// However, If you are porting this component to other O.S., be careful,
// as this is not a standard unix option
setsockopt(SOCKET->socket, SOL_SOCKET, SO_REUSEADDR, &auth, sizeof(int));
// Define specific interface: does not really work... :-/
if (THIS->interface)
{
if (setsockopt(SOCKET->socket, SOL_SOCKET, SO_BINDTODEVICE, THIS->interface, GB.StringLength(THIS->interface)))
{
fprintf(stderr, "unable to bind socket to interface: %s\n", strerror(errno));
SOCKET->status = -15;
return 15;
}
}
SOCKET_update_timeout(SOCKET);
//
if (THIS->type)
retval=bind(SOCKET->socket,(struct sockaddr*)&THIS->so_server.in, \
sizeof(struct sockaddr_in));
else
retval=bind(SOCKET->socket,(struct sockaddr*)&THIS->so_server.un, \
sizeof(struct sockaddr_un));
if (retval==-1)
{
close(SOCKET->socket);
SOCKET->status=-10;
GB.Ref(THIS);
GB.Post(srvsock_post_error,(intptr_t)THIS);
return 10;
}
ioctl(SOCKET->socket,FIONBIO,&NoBlock);
if ( listen(SOCKET->socket,mymax) == -1 )
{
close(SOCKET->socket);
SOCKET->status=-14;
GB.Ref(THIS);
GB.Post(srvsock_post_error,(intptr_t)THIS);
return 14;
}
THIS->iCurConn=0;
THIS->iMaxConn=mymax;
SOCKET->status=1;
//CServerSocket_AssignCallBack((intptr_t)THIS,SOCKET->socket);
if (THIS->type)
GB.Watch (SOCKET->socket , GB_WATCH_READ , (void *)CServerSocket_CallBack,(intptr_t)THIS);
else
GB.Watch (SOCKET->socket , GB_WATCH_READ , (void *)CServerSocket_CallBackUnix,(intptr_t)THIS);
return 0;
}
static void srvsock_listen(CSERVERSOCKET *_object, int max)
{
switch(do_srvsock_listen(THIS, max))
{
case 1: GB.Error("Socket is already listening"); break;
case 7: GB.Error("Path is not defined"); break;
case 8: GB.Error("Port is not defined"); break;
case 13: GB.Error("Invalid maximum number of connections"); break;
case 15: GB.Error("Unable to bind socket to interface"); break;
default: break;
}
}
// BM: Fix bug in allocations
void CServerSocket_NewChild(CSERVERSOCKET *_object, void *cli_obj)
@ -102,64 +263,11 @@ void CServerSocket_OnClose(void *sck)
}
void CServerSocket_CallBack(int fd, int type, intptr_t lParam)
{
int okval=0;
char *rem_ip_buf;
unsigned int ClientLen;
CSERVERSOCKET *_object = (CSERVERSOCKET*)lParam;
if ( SOCKET->status != 1) return;
SOCKET->status=2;
ClientLen=sizeof(struct sockaddr_in);
THIS->Client=accept(SOCKET->socket,(struct sockaddr*)&THIS->so_client.in,&ClientLen);
if (THIS->Client == -1)
{
close(THIS->Client);
SOCKET->status=1;
return;
}
rem_ip_buf=inet_ntoa(THIS->so_client.in.sin_addr);
if ( (!THIS->iMaxConn) || (THIS->iCurConn < THIS->iMaxConn) ) okval=1;
if ( (!THIS->iPause) && (okval) )
GB.Raise(THIS,EVENT_Connection,1,GB_T_STRING,rem_ip_buf,0);
if ( SOCKET->status == 2) close(THIS->Client);
SOCKET->status=1;
}
void CServerSocket_CallBackUnix(int fd, int type, intptr_t lParam)
{
//int position=0;
int okval=0;
unsigned int ClientLen;
CSERVERSOCKET *_object = (CSERVERSOCKET*)lParam;
if ( SOCKET->status != 1) return;
SOCKET->status=2;
ClientLen=sizeof(struct sockaddr_un);
THIS->Client=accept(SOCKET->socket,(struct sockaddr*)&THIS->so_client.un,&ClientLen);
if (THIS->Client == -1)
{
close(THIS->Client);
SOCKET->status=1;
return;
}
if ( (!THIS->iMaxConn) || (THIS->iCurConn < THIS->iMaxConn) ) okval=1;
if ( (!THIS->iPause) && (okval) )
GB.Raise(THIS,EVENT_Connection,1,GB_T_STRING,NULL,0);
if ( SOCKET->status == 2) close(THIS->Client);
SOCKET->status=1;
}
/***************************************************
This property reflects current status of the
socket (closed, listening...)
***************************************************/
BEGIN_PROPERTY ( CSERVERSOCKET_Status )
BEGIN_PROPERTY ( ServerSocket_Status )
GB.ReturnInteger(SOCKET->status);
@ -168,7 +276,7 @@ END_PROPERTY
/******************************************************************
This property gets/sets the port to listen to (TCP or UDP sockets)
******************************************************************/
BEGIN_PROPERTY ( CSERVERSOCKET_Port )
BEGIN_PROPERTY ( ServerSocket_Port )
if (READ_PROPERTY)
{
@ -193,9 +301,7 @@ END_PROPERTY
/***************************************************************
This property gets/sets the address to listen to (UNIX socket)
***************************************************************/
BEGIN_PROPERTY ( CSERVERSOCKET_Path )
char *tmpstr=NULL;
BEGIN_PROPERTY(ServerSocket_Path)
if (READ_PROPERTY)
{
@ -204,13 +310,12 @@ BEGIN_PROPERTY ( CSERVERSOCKET_Path )
}
if (SOCKET->status>0)
{
GB.Error("Path value can not be changed when socket is active");
GB.Error("Path cannot be changed while socket is active");
return;
}
tmpstr=GB.ToZeroString ( PROP(GB_STRING) );
if ( (strlen(tmpstr)<1) || (strlen(tmpstr)>NET_UNIX_PATH_MAX) )
if (PLENGTH() > NET_UNIX_PATH_MAX)
{
GB.Error ("Invalid path length");
GB.Error ("Path is too long");
return;
}
GB.StoreString(PROP(GB_STRING), &THIS->sPath);
@ -218,10 +323,35 @@ BEGIN_PROPERTY ( CSERVERSOCKET_Path )
END_PROPERTY
BEGIN_PROPERTY(ServerSocket_Interface)
if (READ_PROPERTY)
{
GB.ReturnString(THIS->sPath);
}
else
{
if (SOCKET->status>0)
{
GB.Error("Interface cannot be changed while socket is active");
return;
}
if (PLENGTH() > IFNAMSIZ)
{
GB.Error ("Interface name is too long");
return;
}
GB.StoreString(PROP(GB_STRING), &THIS->interface);
}
END_PROPERTY
/***************************************************************
This property gets/sets the socket type (0 -> TCP, 1 -> UNIX)
***************************************************************/
BEGIN_PROPERTY ( CSERVERSOCKET_Type )
BEGIN_PROPERTY ( ServerSocket_Type )
if (READ_PROPERTY)
{
@ -244,84 +374,56 @@ END_PROPERTY
/***********************************************
Gambas object "Constructor"
***********************************************/
BEGIN_METHOD(CSERVERSOCKET_new,GB_STRING sPath;GB_INTEGER iMaxConn;)
BEGIN_METHOD(ServerSocket_new,GB_STRING sPath;GB_INTEGER iMaxConn;)
int retval;
char *buf=NULL;
int nport=0;
int iMax=0;
int iMax;
THIS->iPort=0;
SOCKET->status=0;
THIS->sPath=NULL;
THIS->iPause=0;
THIS->iMaxConn=0;
THIS->iCurConn=0;
THIS->type=1;
THIS->children=NULL;
THIS->nchildren=0;
if (MISSING(sPath)) return;
if (!STRING(sPath)) return;
if (!MISSING(iMaxConn)) iMax=VARG(iMaxConn);
retval=IsHostPath(STRING(sPath),&buf,&nport);
if (!retval)
iMax = VARGOPT(iMaxConn, 0);
switch(IsHostPath(STRING(sPath), LENGTH(sPath), &buf, &nport))
{
GB.Error("Invalid Host / Path string");
return;
}
if (retval==2)
{
THIS->type=0;
buf=GB.ToZeroString ( (GB_STRING*)STRING(sPath) );
if ( (strlen(buf)<1) || (strlen(buf)>NET_UNIX_PATH_MAX) )
{
GB.Error ("Invalid path length");
case 0:
GB.Error("Invalid Host or Path");
return;
}
GB.StoreString((GB_STRING*)STRING(sPath),&THIS->sPath);
return;
}
else
{
if (buf)
{
GB.Free(POINTER(&buf));
GB.Error("Invalid Host String");
return;
}
if (nport<1)
{
GB.Error("Invalid Port value");
return;
}
THIS->type=1;
THIS->iPort=nport;
}
switch(srvsock_listen(THIS,iMax))
{
case 1:
GB.Error("Socket is already listening");
return;
case 7:
GB.Error ("You must define Path");
return;
case 8:
GB.Error ("Error. You must define port");
return;
case 13:
GB.Error ("Invalid maximun connections value");
return;
}
if (buf)
{
GB.Free(POINTER(&buf));
GB.Error("Invalid Host");
return;
}
if (nport<1)
{
GB.Error("Invalid Port");
return;
}
THIS->type=1;
THIS->iPort=nport;
srvsock_listen(THIS, iMax);
break;
case 2:
THIS->type = 0;
if (LENGTH(sPath) >NET_UNIX_PATH_MAX)
{
GB.Error ("Path is too long");
return;
}
GB.StoreString(ARG(sPath), &THIS->sPath);
break;
}
END_METHOD
void close_server(CSERVERSOCKET *_object)
{
CSOCKET *chd;
@ -345,11 +447,11 @@ void close_server(CSERVERSOCKET *_object)
/*************************************************
Gambas object "Destructor"
*************************************************/
BEGIN_METHOD_VOID(CSERVERSOCKET_free)
BEGIN_METHOD_VOID(ServerSocket_free)
close_server(THIS);
GB.FreeString(&THIS->sPath);
GB.FreeString(&THIS->interface);
END_METHOD
@ -357,7 +459,7 @@ END_METHOD
Stops listening (TCP/UDP/UNIX), and closes all client sockets associated
to this server
*********************************************************/
BEGIN_METHOD_VOID(CSERVERSOCKET_Close)
BEGIN_METHOD_VOID(ServerSocket_Close)
close_server(THIS);
@ -366,7 +468,7 @@ END_METHOD
/********************************************************
Do not accept more connections until Resume is used
*********************************************************/
BEGIN_METHOD_VOID(CSERVERSOCKET_Pause)
BEGIN_METHOD_VOID(ServerSocket_Pause)
THIS->iPause=1;
@ -375,117 +477,15 @@ END_METHOD
/********************************************************
Accept connections again
*********************************************************/
BEGIN_METHOD_VOID(CSERVERSOCKET_Resume)
BEGIN_METHOD_VOID(ServerSocket_Resume)
THIS->iPause=0;
END_METHOD
/*********************************************************
Starts listening (TCP/UDP/UNIX)
**********************************************************/
int srvsock_listen(CSERVERSOCKET* _object,int mymax)
{
int NoBlock=1;
int retval;
int auth = 1;
BEGIN_METHOD(ServerSocket_Listen, GB_INTEGER MaxConn)
if ( (!THIS->iPort) && (THIS->type) ) return 8;
if ( SOCKET->status >0 ) return 1;
if (mymax<0) return 13;
if ( (!THIS->type) && (!THIS->sPath) ) return 7;
if (THIS->type)
{
THIS->so_server.in.sin_family=AF_INET;
THIS->so_server.in.sin_addr.s_addr=INADDR_ANY;
THIS->so_server.in.sin_port=htons(THIS->iPort);
SOCKET->socket=socket(PF_INET,SOCK_STREAM,0);
}
else
{
unlink(THIS->sPath);
THIS->so_server.un.sun_family=AF_UNIX;
strcpy(THIS->so_server.un.sun_path,THIS->sPath);
SOCKET->socket=socket(AF_UNIX,SOCK_STREAM,0);
}
if ( SOCKET->socket==-1 )
{
SOCKET->status=-2;
GB.Ref(THIS);
GB.Post(srvsock_post_error,(intptr_t)THIS);
return 2;
}
// thanks to Benoit : this option allows non-root users to reuse the
// port after closing it and reopening it in a short interval of time.
// However, If you are porting this component to other O.S., be careful,
// as this is not a standard unix option
setsockopt(SOCKET->socket, SOL_SOCKET, SO_REUSEADDR, &auth, sizeof(int));
SOCKET_update_timeout(SOCKET);
//
if (THIS->type)
retval=bind(SOCKET->socket,(struct sockaddr*)&THIS->so_server.in, \
sizeof(struct sockaddr_in));
else
retval=bind(SOCKET->socket,(struct sockaddr*)&THIS->so_server.un, \
sizeof(struct sockaddr_un));
if (retval==-1)
{
close(SOCKET->socket);
SOCKET->status=-10;
GB.Ref(THIS);
GB.Post(srvsock_post_error,(intptr_t)THIS);
return 10;
}
ioctl(SOCKET->socket,FIONBIO,&NoBlock);
if ( listen(SOCKET->socket,mymax) == -1 )
{
close(SOCKET->socket);
SOCKET->status=-14;
GB.Ref(THIS);
GB.Post(srvsock_post_error,(intptr_t)THIS);
return 14;
}
THIS->iCurConn=0;
THIS->iMaxConn=mymax;
SOCKET->status=1;
//CServerSocket_AssignCallBack((intptr_t)THIS,SOCKET->socket);
if (THIS->type)
GB.Watch (SOCKET->socket , GB_WATCH_READ , (void *)CServerSocket_CallBack,(intptr_t)THIS);
else
GB.Watch (SOCKET->socket , GB_WATCH_READ , (void *)CServerSocket_CallBackUnix,(intptr_t)THIS);
return 0;
}
BEGIN_METHOD(CSERVERSOCKET_Listen,GB_INTEGER MaxConn;)
int retval;
int mymax=0;
if (!MISSING(MaxConn)) mymax=VARG(MaxConn);
retval=srvsock_listen(THIS,mymax);
switch(retval)
{
case 1:
GB.Error("Socket is already listening");
return;
case 7:
GB.Error ("You must define Path");
return;
case 8:
GB.Error ("Error. You must define port");
return;
case 13:
GB.Error ("Invalid maximun connections value");
return;
}
srvsock_listen(THIS, VARGOPT(MaxConn, 0));
END_METHOD
@ -493,7 +493,7 @@ END_METHOD
To accept a pending connection and delegate it to a Socket object
*******************************************************************/
BEGIN_METHOD_VOID(CSERVERSOCKET_Accept)
BEGIN_METHOD_VOID(ServerSocket_Accept)
CSOCKET *cli_obj;
struct sockaddr_in myhost;
@ -547,7 +547,7 @@ END_METHOD
// BM: Enumeration of child sockets
BEGIN_METHOD_VOID(CSERVERSOCKET_next)
BEGIN_METHOD_VOID(ServerSocket_next)
int *index = (int *)GB.GetEnum();
@ -561,7 +561,7 @@ BEGIN_METHOD_VOID(CSERVERSOCKET_next)
END_METHOD
BEGIN_PROPERTY(CSERVERSOCKET_count)
BEGIN_PROPERTY(ServerSocket_count)
GB.ReturnInteger(THIS->nchildren);
@ -578,28 +578,29 @@ GB_DESC CServerSocketDesc[] =
GB_EVENT("Connection", NULL, "(RemoteHostIP)s", &EVENT_Connection),
GB_EVENT("Error", NULL,NULL, &EVENT_Error),
GB_METHOD("_new", NULL, CSERVERSOCKET_new,"[(Path)s(MaxConn)i]"),
GB_METHOD("_free", NULL, CSERVERSOCKET_free, NULL),
GB_METHOD("Listen",NULL, CSERVERSOCKET_Listen, "[(MaxConn)i]"),
GB_METHOD("Pause", NULL, CSERVERSOCKET_Pause, NULL),
GB_METHOD("Resume", NULL, CSERVERSOCKET_Resume, NULL),
GB_METHOD("Accept","Socket",CSERVERSOCKET_Accept,NULL),
GB_METHOD("Close",NULL,CSERVERSOCKET_Close,NULL),
GB_METHOD("_new", NULL, ServerSocket_new,"[(Path)s(MaxConn)i]"),
GB_METHOD("_free", NULL, ServerSocket_free, NULL),
GB_METHOD("Listen",NULL, ServerSocket_Listen, "[(MaxConn)i]"),
GB_METHOD("Pause", NULL, ServerSocket_Pause, NULL),
GB_METHOD("Resume", NULL, ServerSocket_Resume, NULL),
GB_METHOD("Accept","Socket",ServerSocket_Accept,NULL),
GB_METHOD("Close",NULL,ServerSocket_Close,NULL),
GB_PROPERTY("Type","i<Net,Internet,Local>",CSERVERSOCKET_Type),
GB_PROPERTY("Path","s",CSERVERSOCKET_Path),
GB_PROPERTY("Port", "i", CSERVERSOCKET_Port),
GB_PROPERTY_READ("Status","i",CSERVERSOCKET_Status),
GB_PROPERTY("Type","i",ServerSocket_Type),
GB_PROPERTY("Path","s",ServerSocket_Path),
GB_PROPERTY("Port", "i", ServerSocket_Port),
GB_PROPERTY("Interface", "s", ServerSocket_Interface),
GB_PROPERTY_READ("Status","i",ServerSocket_Status),
GB_METHOD("_next", "Socket", CSERVERSOCKET_next, NULL),
GB_PROPERTY_READ("Count", "i", CSERVERSOCKET_count),
GB_METHOD("_next", "Socket", ServerSocket_next, NULL),
GB_PROPERTY_READ("Count", "i", ServerSocket_count),
GB_PROPERTY("Timeout", "i", CSOCKET_Timeout),
GB_CONSTANT("_IsControl", "b", TRUE),
GB_CONSTANT("_IsVirtual", "b", TRUE),
GB_CONSTANT("_Group", "s", "Network"),
GB_CONSTANT("_Properties", "s", "Type=0,Path,Port"),
GB_CONSTANT("_Properties", "s", "Type=Local,Path,Port"),
GB_CONSTANT("_DefaultEvent", "s", "Connection"),
GB_END_DECLARE

View file

@ -63,10 +63,8 @@ typedef
int Client;
void **children;
int nchildren;
char *interface;
}
CSERVERSOCKET;
int srvsock_listen(CSERVERSOCKET* mythis, int mymax);
void srvsock_post_error(CSERVERSOCKET* mythis);
#endif

View file

@ -181,7 +181,7 @@ int CheckConnection(int Socket)
}
/* free "buf" after using it! */
int IsHostPath(char *sCad,char **buf,int *port)
int IsHostPath(char *sCad, int lenCad, char **buf,int *port)
{
/*******************
0 --> Error
@ -198,7 +198,7 @@ int IsHostPath(char *sCad,char **buf,int *port)
{
return 2;
}
for (myloop=0;myloop<strlen(sCad);myloop++)
for (myloop=0;myloop<lenCad;myloop++)
{
if (sCad[myloop]==':')
{
@ -208,7 +208,7 @@ int IsHostPath(char *sCad,char **buf,int *port)
}
if (npoint != 1) return 0;
for (myloop=npos+1;myloop<strlen(sCad);myloop++)
for (myloop=npos+1;myloop<lenCad;myloop++)
{
if ( (sCad[myloop]<'0') || (sCad[myloop]>'9') ) return 0;
bufport*=10;

View file

@ -53,7 +53,7 @@ void Alloc_CallBack_Pointers(long nobjs,long **objs,long **scks);
int search_by_integer(long *objlist,long nobj,long iData);
int CheckConnection(int Socket);
int IsHostPath(char *sCad,char **buf,int *port);
int IsHostPath(char *sCad, int lenCad, char **buf,int *port);
void correct_url(char **buf,char *protocol);
int ConvertBaudRate(int nBauds);