输出错误信息 是 创建线程设置参直接调用 数和服务函数 服务函数 还可以 是 服务? 否 结 束
图10.4 循环控制模块流程图
5.服务模块(服务器端)
服务模块用于在服务器端为客户端服务,该模块的实现较为简单,主要进行接受和发送数据操作,其实现流程如图10.5所示。首先用0初始化缓冲区response(数组),然后接收来自客户端的数据,判断接收到的数据是否是\SERVER\如果不是则表示不是对应的客户端,如果是则发送数据到客户端。操作结束后关闭套接字。
开 始
初始 化 缓 冲区
接收客户端数据 否 是预定义的 数据?
输出错误信息 是
发送消息到 客户端
关闭套接字
结 束 图 10.5 服务模块实现流程图
6.服务模块(服务器端)
服务模块用于在服务器端为客户端服务,该模块的实现较为简单,主要进行接受和发送数据操作,其实现流程如图10.5所示。首先用0初始化缓冲区response(数组),然后接收来自客户端的数据,判断接收到的数据是否是\SERVER\如果不是则表示不是对应的客户端,如果是则发送数据到客户端。操作结束后关闭套接字。 10.3.2数据结构设计
本程序没有定义结构体,在此仅讲述服务器端和客户端定义的全局变量。 1.服务器端
在服务器端定义了3个全局变量,分别是指向字符的指针hostName、无符号短整型变量maxService和无符号短整型port,各自表示的意义如下。
char *hostName:该指针用于接收主机名选项,可以是IP地址,也可以是主机名。
Unsigned short maxServer:用于存储服务器端最大的服务次数,超过该次数,服务器将终止服务。
Unsigned short port :用于存储服务器端提供的端口号。
这3个变量所存储的值都是表示服务器启动时提供的选项,如果服务器启动时没有提供这些选项,程序将按照默认设置的值启动服务器。 2.客户端
客户端提供了和服务器端累世的两个全局变量,气作用和意义都是和服务器端的相同,只是这两个变量存储的值在程序中没有默认值,需要客户端启动是提供相应的选项。 char* hostName:接收主机名选项。
Undigned short port:用以存储服务客户端提供的端口号。 10.3.3函数功能描述 1.Initial()
函数原型:void initial()
Initial()函数用于初始化服务器端的全局变量,包括hostName、maxServerice和port,分别被初始化为“127.0.0.1”、“3”和“9999”。服务器在启动时,若没有指定这些选项,程序将使用这些默认值启动服务器。 2.InitSockets()
函数原型:int InitSockts(void)
InitSockets()函数用于初始化Winsock。 3.GetArgment()
函数原型:viod GetArgment(int argc,char**argv)
GetArgment()函数用于获取用户提供的选项,在服务器端能获取的参数包括主机名(或IP地址)、最多服务次数和端口号。其中argc表示获取的选项个数,argv用来存储获取的选项值,这个参数的值通过主函数的参数传递过来。 4.ErrorPrint()
函数原型:void ErrorPoint(x)
ErrorPoint()函数用于输出错误信息,该函数调用系统函数WSAGetLastError()来获取错误号。其中X表示错误消息。 5.userHelp()
函数原型:void userHelp()
userHelp()函数用于现实用户帮战。当服务器端启动时,若提供的选项错误,将调用该函数输出用户帮助信息,提供的信息包括选项的格式和类型。
6.LoopControl()
函数原型:int LoopControl(SOCKET listenfd,int isMultiTasking)
LoopControl()函数用于循环控制,当服务器的服务次数在指定的范围内,将接收客户端的请求,并创建一个线程(如果需要的话)来为客户端服务(调用Service()函数)。其中listenfd表示侦听套接字,isMultiTasking是个标记,如果其设置为1,则创建一个线程来服务器端,如果其设置为0,则直接调用服务器函数来服务客户端。 7.Service()
函数原型:void Service(LPVOID lpv)
Service()函数用于服务客户端,包括接收客户端的数据和发送数据到客户端。 2.客户端
客户端的这几个函数在服务器端也出现过,其功能和服务器端的函数类似。 1 InitSockets()
函数原型:int InitSockets(void)
InitSockets()函数用于初始化Winsock。 2 GetArgument()
函数原型:void GetArgument(int argc , char **argv)
GetArguemnt()函数用于获取用户提供的选项,在客户端能获取的参数包括主机名(或IP地址)和端口号。其中argc和argv值也是通过主函数的参数传递过来,其表示的意义和主函数中的一样。 3 ErrorPrint()
函数原型:void Errorprint()
ErrorPrint()函数用于输出错误信息。 4 userHelp()
函数原型:void userHelp()
userHelp()函数用于显示用户帮助。当客户端不带选项启动时或带错误选项启动时将调用该函数显示用户帮助,显示选项的格式和类型。
10.4程序实现
10.4.1 源码分析
1 服务端(service.c) 1 程序预处理
程序处理包括库文件的导入、头文件的加载以及常量和全局变量的定义
/*导入库文件*/ #pragma comment(lib,\/*加载头文件*/ #include
2初始化模块
初始化模块由两部分组成,包括全局变量的初始化和Winsock的初始化,由两个函数来实现
1 void initial(),初始化全局变量,其中hostName被赋值为“127.0.0.1”,表明程序在运行是仅限制客户端和服务器端在同一主机上进行,如果要改变该值,需要在服务器启动是,设置hostName选项,重新赋值;maxService表示最大的服务次数,其值应该不大于MAX SER代表的值。 2 int InitSockets(void),初始化Winsock,包括初始化套接字版本号和加载Winsockku。
/*初始化全局变量函数*/ void initial() { hostName = \ maxService = 3; port = 9999; } /*初始化Winsocket函数*/ int InitSockets(void) { WSADATA wsaData; WORD sockVersion; int err; /*设置Winsock版本号*/ sockVersion = MAKEWORD( 2, 2 ); /*初始化Winsock*/ err = WSAStartup( sockVersion, &wsaData ); /*如果初始化失败*/ if ( err != 0 ) { printf(\ return 1; } return 0 }
3.功能控制模块
功能控制模块提供了参数获取功能、错误输出功能和用户帮助功能,这几个功能分别由GetArgments(int argc ,char **argv),获取用户提供的选项值。该函数首先判断每个参数的第一个字符,如果第一个字符是“-”(短横线)则表示该参数是用户提供的选项。提供的选项包括“-p(-p)”,表示端口号;“-h(-H)”,表示主机名(或者ip地址);“-n(-N)”,表示服务器端的最多服务次数,超过该服务次数服务器将自动停止。 (2) void ErrorPrint(x),错误输出函数。 (3) void userHelp(),显示用户帮助函数。在GetArgments()函数中,如果获取的选项值不是预定义的值,则调用该函数输出用户帮助。
/*获取选项函数*/ void GetArgments(int argc, char **argv) { int i; for(i=1; i < argc ;i++) { /*参数的第一个字符若是“-”*/ if (argv[i][0] == '-') { /*转换成小写*/ switch (tolower(argv[i][1])) { /*若是端口号*/ case 'p': if (strlen(argv[i]) > 3) port = atoi(&argv[i][3]); break; /*若是主机名*/ case 'h': hostName = &argv[i][3]; break; /*最多服务次数*/ case 'n': maxService = atoi(&argv[i][3]); break; /*其他情况*/ default: userHelp(); break; }