TCP程序设计(c语言课程设计)
更新时间:2024-07-11 03:02:01 阅读量: 综合文库 文档下载
第十章
TCP协议,即传输控制协议(Transport Control Protocol),是一种面向连接的、可靠的传输层协议。TCP协议是为了在主机实现高可性包交换的传输协议,在计算机网络中用途很广泛。本章将通过C程序语言编程来实现一个基于TCP协议的程序,旨在向读者介绍TCP的实现原理,并进一步向读者介绍C语言网络编程技术。关于TCP的原理知识,读者可参见第2章。
10.1 设计目的
本章通过C 语言编程实现了一个TCP程序——包括服务器端程序和客户端程序,程序能实现基本的通信。通过本程序向读者展示了TCP的服务器端和客户端的操作流程,用以加深读者对TCP原理的理解。本章的部分知识点在前面章节也有所涉及,读者可以由此加深印象。
通过本章的学习,读者应该对以下知识点有一定的了解:Winsock版本的设置、Winsock库的加载以及Winsock错误号的获取;套接字的创建和关闭;TCP服务器的操作流程、客户端的操作流程;套接字的绑定、侦听、连接和接收操作;数据报的发送和接收;根据地址获取主机、根据主机名获取IP地址等信息;线程饿创建和参书设置;字符串比较函数的使用等。
读者可以在本章的基础上加以拓展,深刻理解TCP原理,掌握TCP编程方法和技巧,开发出自己的TCP程序。
10.2 功能描述
本章用C语言实现了基于TCP的服务器端和客户端程序,能实现基本的TCP通信。
其主要的功能包括如下。
(1) 服务器端能以默认选项(服务器端IP地址或主机名、端口号)启动,提供服务
功能。
(2) 服务器端能根据用户指定的选项(服务器端IP地址或主机名、端口号)启动,
提供服务和功能。
(3) 服务器以错误选项启动时,会提示错误信息,并终止程序。
(4) 客户端能连接到服务器端,发送消息到服务器端,同时也能接收来自服务器的
响应。
(5) 客户端不能连接到服务器端时,能输出错误信息。
(6) 客户端以错误选项启动时,会提示错误信息,并终止程序。
10.3总体设计
10.3.1 功能模块设计
1. 个功能模块图
本程序由两大部分组成,包括服务器端和客户端,如图10.1所示。服务器端包含的模块有初始模块、循环控制模块和服务模块;客户端包含的模块有初始化模块、功能控制模块和数据传输控制模块。
1) 服务器端
(1) 初始化模块用于初始化各个全局变量赋初始值。初始化Winsock,加
载Winsock库。
(2) 功能模块控制。该模块为其他模块提供调用的函数,包括参数获取功
能、用户帮助功能和错误输出功能 。
(3) 循环控制模块。该模块用于控制服务器端的服务次数,如果服务次数
超过指定的值则停止服务器。
(4) 服务模块。该模块为客户端提供服务功能,包括接收来自客户端的数
据,并发送数据到客户端。
2) 客户端
(1) 初始化模块。该模块用于初始化客户端的Winsock,加载Winsock库。 (2) 功能模块控制。与服务器端一样,该模块提供了参数获取、用户帮助
和错误输出功能。
(3) 数据传输控制模块。该模块控制着整个客户端的数据传输,包括数据
发送和接收等。
TCP程序设计 服务器端 客户端 初始化模块 功能控制模块 循环控制模块 服务模块 初始化模块 功能控制模块 数据传输控制模块 图 10.1 功能模块图 2. 服务器端系统流程图
服务器端系统流程图10.2所示。程序首先调用GetArgments()函数获取用户提供的先项,如果没有提供选项,则直接使用默认值,如果有选项提供并成功获取(选项错误则显示用户帮助并终止程序),则初始化变量和Winsock,并创建TCP流套接字;然后解析主机名(如果选项提供的是IP地址 ,或者使用是默认值)或者IP地址(如果选项提供的是主机名),解析成功后则设置服务器地址的各个参数,包括地址、IP
地址和端口号;接下来将创建的TCP流套接字和设定的服务器地址绑定(调用bing()函数);绑定成功后,则开始侦听客户的连接,调用循环控制函数LoopControl()函数和Service()函数作接收客户端的连接,接收数据、发送数据等操作;当服务数达到最多的服务次数时,并提示错误信息(调用ErrorPrint()函数实现)。
否
开始 获取参数 否 获取成功 是 初始化变量和Winsock 创建套接字 否 创建成功 是 解析主机名或者IP地址 否 解析成功 是 设置服务器地址参数 绑定地址与接字 否 绑定成功 是 侦听连接 侦听成功 是` 循环控制
输出相应
错误信息
图10.2 服务器端系统流程图 3. 客户端系统流程图
客户端系统流程图如图10.3所示。客户端程序执行时必须带选项,程序首先判断用户提供的参数个数,如果参数不等于3个,则比表明用户没有提供正确的选项,退出程序;如果参数等于3个,则调用GetArgments()函数获取用户提高的选项,如果获取的选项错误则显示用户帮助并终止程序,如果选项正确则开始创建TCP流套接字,成功创建TCP流套接字后则作和服务器类似的操作,即解析主机名或IP地址、设置服务器端地址;然后进行连接服务器操作,若能成功连接则输出连接信息,并发送消息到服务器端;然后接收来自服务器端的响应,(消息),并将接收到的消息输出。最后关闭套接字和释放占用的资源。和服务器一样,在操作过程中,任何一步操作失败都将退出程序,并提示错误信息(调用ErrorPrint()函数实现)。 显示用 户帮 助 开 始 否 释放资源关闭服务 结束 3个参是 获 取 参 数 否 获取成功 是 创 建 套 接 字 否 创建成功 解析主机名或者IP地址 否 解析成功 设置服务器地址参数 是 是
连 接服务否 连接成是 输出连接信息 发送 信息 到服 务 器 端 接收服务器端的响应 输出相应错误信息 释 放 资 源 关闭 套 接 字 结束 图 10.3 客户端系统流程图
4. 循环控制模块(服务器端)
该模块是服务器端用于循环控制的模块,其操作流程如图10.4所示。当服务器端侦听到客户连接时,调用该模块进行操作。首先接收客户端的请求,接收成功后,根据传入的参数is Multitasking判断是否要创建一个线程来服务客户端,如果 is Multitasking是1则创建线程来服务客户端(创建新线程时,设置了的初始堆栈大小为1000,线程执行函数是Service(),传递给Service()的参数为接收套接字),如果is Multitasking是0则直接调用 Service()函数来服务客户端。一次服务成功后,判断循环次数是否小于最大服务次数(可使用默认值,也可使用参数形式提供),如果已达到最大服务次数则关闭服务器,否则继续进行下一次服务。
开 始 接收客户端请求 否 接收成功? 是 否 创建线程?
输出错误信息 是 创建线程设置参直接调用 数和服务函数 服务函数 还可以 是 服务? 否 结 束
图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; }
} } return; } /*错误输出函数*/ void ErrorPrint(x) { printf(\} /*用户帮助函数*/ void userHelp() { printf(\ printf(\ printf(\ printf(\ printf(\ printf(\\\n\ printf(\ ExitProcess(-1); } 4) 循环控制模块
循环控制模块的功能是由LoopControl()函数实现的。具体步骤可参见10.3.3节中的函数功能描述其操作流程图可参见图10.4。
/*循环控制函数*/ int LoopControl(SOCKET listenfd, int isMultiTasking) { SOCKET acceptfd; struct sockaddr_in clientAddr; int err; int nSize; int serverNum = 0; HANDLE handles[MAX_SER]; int myID; /*服务次数小于最大服务次数*/ while (serverNum < maxService) { nSize = sizeof(clientAddr); /*接收客户端请求*/ acceptfd = accept(listenfd, (struct sockaddr *) &clientAddr, &nSize); /*如果接收失败*/ if (acceptfd == INVALID_SOCKET) { ErrorPrint(\ return 1; } /*接收成功*/ printf(\ inet_ntoa(clientAddr.sin_addr)); /*如果允许多任务执行*/ if (isMultiTasking) { /*创建一个新线程来执行任务,新线程的初始堆栈大小为1000,线程执行函数 是Service(),传递给Service()的参数为acceptfd*/ handles[serverNum] = CreateThread(NULL, 1000, (LPTHREAD_START_ROUTINE)Service, (LPVOID) acceptfd, 0, &myID); } else /*直接调用服务客户端的函数*/ Service((LPVOID) acceptfd); serverNum++; } if (isMultiTasking) { /*在一个线程中等待多个事件,当所有对象都被通知时函数才会返回,并且等待没有时间限制*/ err = WaitForMultipleObjects(maxService, handles, TRUE, INFINITE); printf(\ } return 0; } 5) 服务模块
服务模块的功能由函数Service()来实现。其功能主要是接收、判断来自客户端的数据,以及发送数据到客户端。Service()函数首先接收客户端发送来的数据,存放到缓冲区response中,然后判断接收到的数据是否和预定义的数据“HELLO SERVER”相同,如果相同则发送消息到客户端,并关闭套接字;否则,输出错误信息并关闭套接字。其实现流程图可参见图10.5。
/*服务函数*/ void Service(LPVOID lpv) { SOCKET acceptfd = (SOCKET) lpv; const char *msg = \ char response[4096]; /*用0初始化response[4096]数组*/ memset(response, 0, sizeof(response)); /*接收数据,存入response中*/ recv(acceptfd, response, sizeof(response), 0); /*如果接收到的数据和预定义的数据不同*/ if (strcmp(response, \ { printf(\ \ } else /*发送服务器端信息到客户端*/ send (acceptfd, msg, strlen(msg)+1, 0); /*关闭套接字*/ closesocket(acceptfd); } 6) 主函数
主函数控制着整个程序的流程,包括套接字的创建、绑定、侦听和释放,以及对各个模块中函数的调用等。其具体操作流程图可参见图10.2。
/*主函数*/ int main(int argc, char **argv) { SOCKET listenfd; int err; struct sockaddr_in serverAddr; struct hostent *ptrHost; initial(); GetArgments(argc,argv); InitSockets(); /*创建TCP流套接字,在domain参数为PF_INET的SOCK_STREAM套接口中,protocol参数为0意味 着告诉内核选择 IPPRPTP_TCP,这也意味着套接口将使用TCP/IP协议*/ listenfd = socket(PF_INET, SOCK_STREAM, 0); /*如果创建套接字失败*/ if (listenfd == INVALID_SOCKET) { printf(\ return 1; } /*如果是IP地址*/ if (atoi(hostName)) { /*将IP地址转换成32二进制表示法,返回32位二进制的网络字节序*/ u_long ip_addr = inet_addr(hostName); /*根据IP地址找到与之匹配的主机名*/ ptrHost = gethostbyaddr((char *)&ip_addr, sizeof(u_long), AF_INET); } /*如果是主机名*/ else /*根据主机名获取一个指向hosten的指针,该结构中包含了该主机所有的IP地址*/ ptrHost = gethostbyname(hostName); /*如果解析失败*/ if (!ptrHost) { ErrorPrint(\ return 1; } /*设置服务器地址*/ /*设置地址族为PF_INET*/ serverAddr.sin_family = PF_INET; /*将一个通配的Internet地址转换成无符号长整型的网络字节序数*/ serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); /*将端口号转换成无符号短整型的网络字节序数*/ serverAddr.sin_port = htons(port); /*将套接字与服务器地址绑定*/ err = bind(listenfd, (const struct sockaddr *) &serverAddr, sizeof(serverAddr)); /*如果绑定失败*/ if (err == INVALID_SOCKET) { ErrorPrint(\ return 1; } /*开始侦听,设置等待连接的最大队列长度为SOMAXCONN,默认值为5个*/ err = listen(listenfd, SOMAXCONN); /*如果侦听失败*/ if (err == INVALID_SOCKET) { ErrorPrint(\ return 1; } LoopControl(listenfd, 1); printf(\ /*释放Winscoket初始化时占用的资源*/ WSACleanup(); return 0; } 2. 客户端(client.c) 1) 程序预处理
与服务器一样,客户端的预处理也包括库文件的导入、头文件的加载和全局变量的定义。
/*导入库文件*/ #pragma comment(lib,\/*加载头文件*/ #include
因为不存在对全局变量赋初始值,所以客户端的初始化模块仅仅初始化Winsock,包括初始化套接字版本号加载Winsock库。
/*初始化Winsock函数*/ 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) 功能控制模块
功能控制模块包括选项获取功能、错误输出功能和用户帮助功能。这几个功能分别由GetArgment()函数、ErrorPrint()函数和userHelp()函数来实现,这几个函数和服务器端的函数功能、参数意义相同,在此就不再赘述。
(1) void GetArgment(int argc,char **argv),获取用户提供的选项。 (2) void ErrorPrint(x),输出错误信息。 (3) void userHelp(),显示用户帮助。
/*获取选项函数*/ 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; /*其他情况*/ default: userHelp(); break; } } } return; } /*错误输出函数*/ void ErrorPrint(x) { printf(\} /*用户帮助函数*/ void userHelp() { printf(\ printf(\ printf(\ ExitProcess(-1); } 4) 数据传输控制模块
客户端程序把数据的输入输出部分都放在主函数中执行,即数据传输控制由主函数来实现。主函数中包括套接字的创建、绑定和释放,服务器的连接,数据的发送、接收以及对各个模块中函数的调用等。其具体操作流程图可参见图10.3。
/*主函数*/ int main(int argc, char **argv) { SOCKET clientfd; int err; struct sockaddr_in serverAddr; struct hostent *ptrHost; char response[4096]; char *msg = \ GetArgments(argc, argv); if (argc != 3) { userHelp(); return 1; } GetArgments(argc,argv); InitSockets(); /*创建套接字*/ clientfd = socket(PF_INET, SOCK_STREAM, 0); /*如果创建失败*/ if (clientfd == INVALID_SOCKET) { ErrorPrint(\ return 1; } /*根据IP地址解析主机名*/ if (atoi(hostName)) { u_long ip_addr = inet_addr(hostName); ptrHost = gethostbyaddr((char *)&ip_addr, sizeof(u_long), AF_INET); } /*根据主机名解析IP地址*/ else ptrHost = gethostbyname(hostName); /*如果解析失败*/ if (!ptrHost) { ErrorPrint(\ return 1; } /*设置服务器端地址选项*/ serverAddr.sin_family = PF_INET; memcpy((char *) &(serverAddr.sin_addr), ptrHost->h_addr,ptrHost->h_length); serverAddr.sin_port = htons(port); /*连接服务器*/ err = connect(clientfd, (struct sockaddr *) &serverAddr, sizeof(serverAddr)); /*连接失败*/ if (err == INVALID_SOCKET) { ErrorPrint(\ return 1; } /*连接成功后,输出信息*/ printf(\ /*发送消息到服务器端*/ send (clientfd, msg, strlen(msg)+1, 0); memset(response, 0, sizeof(response)); /*接收来自服务器端的消息*/ recv(clientfd, response, sizeof(response), 0); printf(\ /*关闭套接字*/ closesocket(clientfd); /*释放Winscoket初始化时占用的资源*/ WSACleanup(); return 0; } 提示: 由于在TC或者Win-TC中没有编译套接字的头文件,所以该程序需要在
Visual C++或者具有Winsock头文件的编译器中编译。本章的服务器端和客户端程序端都已经在Visual C++6.0中通过编译。 10.4.2 运行结果
本节将对服务器端和客户端从两个大方面进行测试,包括错误测试和带选项(带正确选项值)的测试。
1. 错误测试
由于服务器端可以不带选项进行启动,所以对服务器端的错误测试主要是带错误选项的测试;而客户端的错误测试包括不带选项启动、带不正确的端口号或者主机名启动,以及服务器未启动时启动客户端。 1) 服务器端选项错误
如图10。6所示,服务器端错误选项(“-1“)启动时,则会显示用户帮助信息(选项格式和类型),并终止程序。
C:\\WINDOWS\\system32\\cmd.exe E:\\book\\str\\chapt10\\tcp\\Debug>tcp.exe -1 userHelp: -h:str –p:int –n:int -h:str The host name The default host is 127.0.0.1 -p:int The Port number to use The default port is 9999 -n:int The number of service,below MAX_SER The default number is 3 E:\\book\\str\\chap10\\tcp\\Debug> 图10。6 带错误选项的服务器端启动
2) 客户端不带选项
客户端启动时必须带选项(服务器端IP地址或者主机名、端口号),如果不带选项启动则会出错并终止程序。如图10。7所示,不带选项启动客户端,将显示用户帮助信息(选项格式和类型)。
3) 服务器未启动时,启动客户端
如果未启动服务器时就启动客户端,将不能正确连接到服务端。如图10。8所示,不能连接到服务器端,并显示出错信息。 4) 客户端端口号或者主机名不正确 如果服务器端已经启动(这里已经以默认选项启动服务端,即服务器端IP地址为“127。0。0。1”,端口号为“9999”),但是客户端启动时端口号、服务器IP地址或主机名有一个不正确将不能正确连接到服务端。
C:\\WINDOWS\\system32\\cmd.exe E:\\book\\str\\chap10\\tcp_client\\Debug\\tcp_client.exe userHelp: -h:str –p:int -h:str The host name -p:int The Port number to use E:\\book\\str\\chap10\\tcp_client\\Debug> 图10。7 不带选项的客户端启动
C:\\WINDOWS\\system32\\cmd.exe E:\\book\\str\\chap10\\tcp_client.exe –h:127.0.0.1 –p:9999 Error 10061:cannot connent to server E:\\book\\str\\chap10\\tcp_client\\Debug>
图10。8 服务器未启动时启动客户端
如图10。9所示,客户端以“-h:127.0.01 –p:88 ”启动,由于服务器端的端口号是“9999”,所以这里不能正确连接。
C:\\WINDOWS\\system32\\cmd.exe E:\\book\\str\\chap10\\tcp_clieng\\Debug\\tcp_client.exe –h:127.0.0.1 –p:888 Error 10061:cannot connect to server E:\\book\\str\\chap10\\tcp_client\\Debug> 图10。9 带不正确的端口号启动客户端
如图10。10所示,客户端以“-h:127.0.0.2 –p:9999”启动,虽然端口号正确,但是服务器端的IP地址不正确,所以也不能正确连接。
C:\\WINDOWS\\system32\\cmd.exe E:\\book\\src\\chap10\\tcp_client\\Debug>tcp_client.exe –h:127.0.02 –P:9999 Error 11004: cannot resolve hostname E:\\book\\src\\chap10\\tcp_client\\Debug>_
图 10.10 带不正确的IP地址启动客户端
如图10.11所示,客户端以“-h:kkk –P:9999”启动,虽然端口正确,但是服务器端的主机名不正确,所以仍然不然正确连接
C:\\WINDOWS\\system32\\cmd.exe E:\\book\\src\\chap10\\tcp_client\\Debug>tcp_client.exe –h:kkk –P999 Error 11001: cannot resolve hostname E:\\book\\src\\chap10\\tcp_client\\Debug>-
图10.11 带不正确的主机名启动客户端 2. 带正确选项的测试
1) 以默认主机名和端口号启动服务器
如图10.12所示,以默认选项启动服务器端,即服务器端IP地址为“127.0.1”,端口号为“9999”。如果客户端有到服务器端的连接,则在客户端会显示连接信息,信息中包括客户端的IP地址。图10.12中显示的信息“Accepeted connection from client at##127.0.0.1”,
由于这里是在同一台主机上,则这里将显示相应的客服端IP地址,但前提是服务器端不是以“127.0.0.1”为地址启动,而是相应的服务器端所在主机的IP地址或者主机名为地址
来启动。
启动服务器端后,以正确的服务器端IP地址和端口号启动客户端,如图10.13所示。这时将在客户端显示连接信息,并显示来自服务器端的相应“HELLO CLIERNT".而服务器端的连接信息则如图10.12所示。
同样的,在客户端以正确的服务器端主机名和端口号启动客户端,仍会正确连接,如图10.14所示,其显示的连接信息也和图10.13相同。
C:\\WINDOWS\\system32\\cmd.exe – tcp.exe E:\\book\\src\\chap10\\tcp\\Debug>tcp.exe Accepted connection from client at 127.0.0.1 -
图10.12 以默认主机名和端口号启动服务器
C:\\WINDOWS\\system32\\cmd.exe E:\\book\\src\\chap10\\tcp_client\\Debug>tcp_client.exe –h:127.0.0.1 –P:9999 You are connected to the server Server says HELLO CLIENT E:\\book\\\\syc\\chap10\\tcp_client\\Debug>_
图10.13 带正确IP地址和端口号启动客户端
C:WINDOWS\\system32\\cmd.exe E:\\book\\src\\chap10\\tcp_client\\Debug>tcp_client.exe –h:computer –P9999 You are connected to the server Server says HELLO CLIENT E:\\book\\src\\chap10\\tcp_client\\Debug>_ 图10.14带正确主机名和端口号启动客户端
如果客户端连接3次服务器后,达到服务器端的最大服务次数(默认值为3次),服务器端将会自动关闭,如图10.15所示。 2)带选项启动服务器
服务器端也可以以指定选项的方式启动,如图10.16所示,服务器端以命令”tcp.exe-h:127.0.0.1-p:888-n:4”启动,表示服务器端的IP地址是“127.0.0.1”,端口号是“8888”,做多服务次数是4次,图10.16中显示的是服务器在服务4次后关闭的情形,其户端的连接操作和前面一样,只是连接的端口号必须设置为“8888”.
C:\\WINDOWS\\system32\\cmd.exe E:\\book\\src\\chap10\\tcp\\Debug>tcp.exe Accepted connection from client at 127.0.0.1 Accepted connection from client at 127.0.0.1 Accepted connection from client at 127.0.0.1 Last thread to finish was thread #0 Server is down E:\\book\\src\\chap10\\tcp\\Debug>_
图10.15 达到最多服务次数的服务器端
C\\WINDOWS\\system32\\cmd.exe E:\\book\\\\src\\chap10\\tcp\\Debug>tcp.exe –h:127.0.0.1 –P:888 –n:4 Accepted connection from client at 127.0.0.1 Accepted connection from client at 127.0.0.1 Accepted connection from client at 127.0.0.1 Accepted connection from client at 127.0.0.1 Last thread to finish was thread #0 Server is down E:\\book\\src\\chap10\\tcp\\Debug>_ 图10.16 带正确选项启动服务器端 10.5 小 结
本章向读者展示了一个基于TCP原理的服务器端和客户端程序的实现过程,分别介绍了服务器端和客户端的实现的方法、实现步骤、并通过源码分析详细介绍了服务器端和客户端的实现过程。通过本章学习,读者应该掌握以下知识点。 (1)Winsock的相关设置。 (2)套接字的创建、关闭。
(3)TCP程序服务器的操作,包括绑定、侦听和接受操作。 (4)TCP程序客户端的操作。 (5)数据报的发送和接受操作。 (6)线程的创建和设置等。
正在阅读:
TCP程序设计(c语言课程设计)07-11
珍爱生命 构建平安校园07-02
公司员工2022年个人工作总结范文03-24
政风行风“回头看”评议工作方案04-18
创业计划书08-22
3D打印论文中英文资料外文翻译文献09-06
小学语文S版六年级上册课文03-04
纪检监察干部谈心谈话记录内容07-31
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 程序设计
- 语言
- 课程
- 设计
- TCP
- 宁波中学2014-2015学年高一下学期期中考试化学试题及答案
- 物理学毕业论文
- 财务岗位实习日记20篇
- 细胞色素P450亚酶CYP2D6AMMC活性荧光定量检测试剂盒
- 西南科技大学远程教育家庭理财答案
- 外研版英语必修6 Module3 Period 4 Function and speaking-Talki
- 新时期发挥共青团组织桥梁纽带作
- 基于蛋白质组学的细胞色素P450和葡萄糖醛酸转移酶亚型绝对定量分
- 财务会计实习报告10篇
- 2017福建省中小学幼儿园教师职务申报表
- 细胞色素P450酶诱导模型
- 基于“目标—评价—教学”一致性的教学设计
- 2010年经济师《人力资源管理专业知识与实务》考试大纲
- 理论知识例题3套
- 天津2015年一级建造师:工程经济六考试题
- 磁县鑫宝简介(模板)
- 九年级物理第一轮中考总复习教学案(1)
- 中国石油大学远程教育职业汉语网上大作业
- 生物化学知识点总结
- 中空膜项目可行性研究报告(案例模板) - 图文