AI智能
改变未来

Socket基础三:基于流式套接字的网络程序(服务器回射程序设计)

作者:刘磊 2020.4.27

参考书目:《Windows网络编程》刘琰等著

  • 回射程序

回射程序是进行网络诊断的常用工具之一,例如Ping是windows系统下自带的可执行命令,利用它可以检查网络是否连通,不过ping是网络层上的,不适合传输层的应用程序测试和诊断。

 

  • 基本执行步骤

服务器:

  1. 引用头文件
  2. 创建流式套接字
  3. 捆绑服务器的指定端口到套接字
  4. 把套接字变换成监听套接字
  5. 接收客户连接
  6. 接收客户发来的数据
  7. 发送客户发来的数据
  8. 回到步骤6
  9. 如果客户端关闭连接,则终止当前连接
  10. 回到步骤5
  11. 如果终止条件到,则关闭套接字,释放资源,关闭程序。

客户机:

  1. 引用头文件
  2. 处理命令行参数
  3. 创建流式套接字
  4. 指定服务器IP地址和端口
  5. 与服务器建立连接
  6. 获得用户输入
  7. 发送回射请求
  8. 接收并输出服务器应答
  9. 回到步骤6
  10. 如果终止条件到,则关闭套接字,释放资源,终止程序

 

  • 对回射功能进行编码

1)tcp_server_fun_echo(SOCKET s)编程代码如下:

[code]//回射函数int tcp_server_fun_echo(SOCKET s){int iResult = 0;char recvline[MAXLINE];do {memset(recvline, 0, MAXLINE);//接收数据iResult = recv(s, recvline, MAXLINE, 0);if (iResult > 0){printf(\"服务器接收到数据%s\\n\", recvline);//回射发送已收到的数据iResult = send( s, recvline, iResult, 0);if (iResult == SOCKET_ERROR){printf(\"send 函数调用错误,错误号:%d\\n\", WSAGetLastError());return 1;}elseprintf(\"对方连接关闭,退出\\n\");}else{if (iResult == 0)printf(\"对方连接关闭,退出\\n\");else{printf(\"recv 函数调用错误,错误号:%d\\n\", WSAGetLastError());return 1;}break;}} while (iResult > 0);return iResult;}

2)tcp_client_fun_echo(FILE *fp,SOCKET s)编程代码如下:

[code]//回射函数int tcp_client_fun_echo(FILE *fp, SOCKET s){int iResult;char sendline[MAXLINE], recvline[MAXLINE];memset(sendline, 0, MAXLINE);memset(recvline, 0, MAXLINE);while (fgets(sendline, MAXLINE, fp) != NULL){if (*sendline == \'Q\'){printf(\"input end!\\n\");iResult = shutdown(s, SD_SEND);if (iResult == SOCKET_ERROR){printf(\"shutdown failed with error:%d\\n\", WSAGetLastError());}return 0;}iResult = send(s, sendline, strlen(sendline), 0);if (iResult == SOCKET_ERROR){printf(\"send 函数调用错误,错误号:%d\\n\", WSAGetLastError());return 1;}printf(\"\\r\\n客户端发送数据:%s\\r\\n\", sendline);memset(recvline,0,MAXLINE);iResult = recv(s,recvline,MAXLINE,0);if (iResult > 0)printf(\"客户端接收到数据 %s \\r\\n\",recvline);else{if (iResult == 0)printf(\"服务器终止!\\n\");elseprintf(\"recv 函数调用错误,错误号:%d\\n\", WSAGetLastError());break;}memset(sendline, 0, MAXLINE);}return iResult;}
  • 主函数

1、server.c

[code]#include \"mysocket.h\"#define ECHOPORT \"7210\"int main(int argc, char *argv[]){int iResult = 0;char ip[] = \"127.0.0.1\";SOCKET ListenSocket, ConnetSocket;start_up();ListenSocket = tcp_server( ip, ECHOPORT);if (ListenSocket == -1)return 1;printf(\"服务器准备好回射服务......\\n\");for (; ; ){ConnetSocket = accept(ListenSocket, NULL, NULL);if (ConnetSocket != INVALID_SOCKET){printf(\"\\r\\n建立连接成功\\n\\n\");iResult = tcp_server_fun_echo(ConnetSocket);if (iResult == -1)printf(\"当前连接已关闭或出错\\n\");}else{printf(\"accept 函数调用错误,错误号:%d\\n\",WSAGetLastError());quit(ListenSocket);return 1;}if(closesocket(ConnetSocket) == SOCKET_ERROR)printf(\"closesocket 函数调用错误,错误号:%d\\n\", WSAGetLastError());}quit(ListenSocket);return 0;}

2、client.c

[code]#include \"mysocket.h\"#define ECHOPORT \"7210\"int main(int argc, char *argv[]){int iResult = 0;SOCKET ClientSocket;start_up();printf(\"连接建立成功,请输入回射字符串......\\n\");ClientSocket = tcp_client((char *)argv[1], ECHOPORT);if (ClientSocket == -1)return 1;iResult = tcp_client_fun_echo(stdin,ClientSocket);quit(ClientSocket);return iResult;}

3、mysocket.h

[code]#pragma once#include <time.h>#include <winsock2.h>#include <stdio.h>#pragma comment(lib,\"ws2_32.lib\");using namespace std;#define MAXLINE 4096	//接收缓冲区长度#define LISTENQ 1024	//监听队列长度#define SERVER_PORT 13	//时间同步服务器端口号int start_up(void);		//初始化Windows Sockets DLL;协商版本号int clean_up(void);		//windows sockets资源释放函数int set_address(char *hname, char * sname, struct sockaddr_in * sap, char * protocol);		//地址转换函数int quit(SOCKET s);		//退出处理函数SOCKET tcp_server(char *hname, char *sname);		//服务器初始化函数(字符类型)SOCKET tcp_server(ULONG uIP, USHORT uPort);			//服务器初始化函数(无符号长整型)SOCKET tcp_client(char *hname, char *sname);		//客户端初始化函数(字符类型)SOCKET tcp_client(ULONG uIP, USHORT uPort);			//客户端初始化函数(无符号长整型)int tcp_server_fun_echo(SOCKET s);		//回射函数int tcp_client_fun_echo(FILE *fp,SOCKET s);		//回射函数

4、mysocket.c

[code]#include \"mysocket.h\"//初始化Windows Sockets DLL;协商版本号int start_up(void){//初始化Windows Sockets DLL;协商版本号WORD wVersionRequested;WSADATA wsaData;int iResult;//使用MAKEWORD(lowbyte, highbyte) 宏,在windef.h中声明wVersionRequested = MAKEWORD(2, 2);iResult = WSAStartup(wVersionRequested, &wsaData);if (iResult != 0){printf(\"WSAStartup函数调用错误,错误号:%d\\n\", WSAGetLastError());return -1;}else{printf(\"初始化成功!\\n\");}return 0;}//windows sockets资源释放函数int clean_up(void){int iResult;iResult = WSACleanup();if (iResult == SOCKET_ERROR){printf(\" WSACleanup函数调用错误,错误号:%d\\n\", WSAGetLastError());return 1;}elseprintf(\"WSACleanup 函数调用错误,错误号:%d\\n\", WSAGetLastError());return 0;}//地址转换函数int set_address(char *hname, char * sname, struct sockaddr_in * sap, char * protocol){struct servent *sp;struct hostent *hp;char *endptr;unsigned short port;unsigned long ulAddr = INADDR_NONE;//将地址结构socketaddr_in初始化为0,并设置地址为AF_INETmemset( sap, 0, sizeof( *sp));sap->sin_family = AF_INET;if (hname != NULL){//如果hname不为空,假定给出的hname为点分十进制表示的数字地址,转换地址为sockaddr_in类型ulAddr = inet_addr(hname);if (ulAddr == INADDR_NONE || ulAddr == INADDR_ANY){//调用错误,表明给出的是主机名,调用gethostbyname获得主机地址hp = gethostbyname(hname);if (hp = NULL){printf(\"未知的主机名,错误号:%d\\n\", WSAGetLastError());return -1;}sap->sin_addr = *(struct in_addr *)hp->h_addr;}elsesap->sin_addr.S_un.S_addr = ulAddr;}elsesap->sin_addr.s_addr = htonl(INADDR_ANY);port = (unsigned short)strtol(sname, &endptr, 0);if (*endptr == \'\\0\'){sap->sin_port = htons(port);}else{sp = getservbyname(sname, protocol);if (sp == NULL){printf(\"未知的服务,错误号;%d\\n\",WSAGetLastError());return -1;}sap->sin_port = sp->s_port;}return 0;}//退出处理函数int quit(SOCKET s){int iResult = 0;iResult = closesocket(s);if (iResult == SOCKET_ERROR){printf(\"closesocket 函数调用错误,错误号:%d\\n\", WSAGetLastError());return -1;}iResult = clean_up();return iResult;}//服务器初始化函数(字符类型)SOCKET tcp_server(char *hname, char *sname){sockaddr_in local;SOCKET ListenSocket;const int on = 1;int iResult = 0;//为服务器的本地地址local设置用户输入的IP和端口号if (set_address(hname, sname, &local, \"tcp\") != 0)return -1;//创建套接字ListenSocket = socket(AF_INET, SOCK_STREAM, 0);if (ListenSocket == INVALID_SOCKET){printf(\"socket函数调用错误,错误号:%d\\n\",WSAGetLastError());clean_up();return -1;}//绑定服务器地址iResult = bind(ListenSocket, (struct sockaddr *) & local, sizeof(local));if (iResult == SOCKET_ERROR){printf(\"bind 函数调用错误,错误号:%d\\n\", WSAGetLastError());quit(ListenSocket);return -1;}//设置服务器位监听状态,监听队列长度为LISTENQiResult = listen(ListenSocket, SOMAXCONN);if (iResult == SOCKET_ERROR){printf(\"listen 函数调用错误,错误号:%d\\n\", WSAGetLastError());quit(ListenSocket);return -1;}return ListenSocket;}//服务器初始化函数(无符号长整型)SOCKET tcp_server(ULONG uIP, USHORT uPort){sockaddr_in local;SOCKET ListenSocket;const int on = 1;int iResult = 0;//为服务器的本地地址local设置用户输入的IP和端口号memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_addr.S_un.S_addr = htonl(uIP);local.sin_port = htons(uPort);//创建套接字ListenSocket = socket(AF_INET, SOCK_STREAM, 0);if (ListenSocket == INVALID_SOCKET){printf(\"socket函数调用错误,错误号:%d\\n\", WSAGetLastError());clean_up();return -1;}//绑定服务器地址iResult = bind(ListenSocket, (struct sockaddr *) & local, sizeof(local));if (iResult == SOCKET_ERROR){printf(\"bind 函数调用错误,错误号:%d\\n\", WSAGetLastError());quit(ListenSocket);return -1;}//设置服务器位监听状态,监听队列长度为LISTENQiResult = listen(ListenSocket, SOMAXCONN);if (iResult == SOCKET_ERROR){printf(\"listen 函数调用错误,错误号:%d\\n\", WSAGetLastError());quit(ListenSocket);return -1;}return ListenSocket;}//客户端初始化函数(字符类型)SOCKET tcp_client(char *hname, char *sname){struct sockaddr_in peer;SOCKET ClientSocket;int iResult = 0;//为服务器的本地地址local设置用户输入的IP和端口号if (set_address(hname, sname, &peer, \"tcp\") != 0)return -1;//创建套接字ClientSocket = socket(AF_INET, SOCK_STREAM, 0);if (ClientSocket == INVALID_SOCKET){printf(\"socket函数调用错误,错误号:%d\\n\", WSAGetLastError());clean_up();return -1;}//绑定服务器地址iResult = connect(ClientSocket, (struct sockaddr *) & peer, sizeof(peer));if (iResult == SOCKET_ERROR){printf(\"bind 函数调用错误,错误号:%d\\n\", WSAGetLastError());quit(ClientSocket);return -1;}return ClientSocket;}//客户端初始化函数(无符号长整型)SOCKET tcp_client(ULONG uIP, USHORT uPort){struct sockaddr_in peer;SOCKET ClientSocket;int iResult = 0;//为服务器的本地地址local设置用户输入的IP和端口号memset(&peer, 0, sizeof(peer));peer.sin_family = AF_INET;peer.sin_addr.S_un.S_addr = htonl(uIP);peer.sin_port = htons(uPort);//创建套接字ClientSocket = socket(AF_INET, SOCK_STREAM, 0);if (ClientSocket == INVALID_SOCKET){printf(\"socket函数调用错误,错误号:%d\\n\", WSAGetLastError());clean_up();return -1;}//绑定服务器地址iResult = connect(ClientSocket, (struct sockaddr *) & peer, sizeof(peer));if (iResult == SOCKET_ERROR){printf(\"bind 函数调用错误,错误号:%d\\n\", WSAGetLastError());quit(ClientSocket);return -1;}return ClientSocket;}//回射函数int tcp_server_fun_echo(SOCKET s){int iResult = 0;char recvline[MAXLINE];do {memset(recvline, 0, MAXLINE);//接收数据iResult = recv(s, recvline, MAXLINE, 0);if (iResult > 0){printf(\"服务器接收到数据%s\\n\", recvline);//回射发送已收到的数据iResult = send( s, recvline, iResult, 0);if (iResult == SOCKET_ERROR){printf(\"send 函数调用错误,错误号:%d\\n\", WSAGetLastError());return -1;}elseprintf(\"对方连接关闭,退出\\n\");}else{if (iResult == 0)printf(\"对方连接关闭,退出\\n\");else{printf(\"recv 函数调用错误,错误号:%d\\n\", WSAGetLastError());return -1;}break;}} while (iResult > 0);return iResult;}//回射函数int tcp_client_fun_echo(FILE *fp, SOCKET s){int iResult;char sendline[MAXLINE], recvline[MAXLINE];memset(sendline, 0, MAXLINE);memset(recvline, 0, MAXLINE);while (fgets(sendline, MAXLINE, fp) != NULL){if (*sendline == \'Q\'){printf(\"input end!\\n\");iResult = shutdown(s, SD_SEND);if (iResult == SOCKET_ERROR){printf(\"shutdown failed with error:%d\\n\", WSAGetLastError());}return 0;}iResult = send(s, sendline, strlen(sendline), 0);if (iResult == SOCKET_ERROR){printf(\"send 函数调用错误,错误号:%d\\n\", WSAGetLastError());return -1;}printf(\"\\r\\n客户端发送数据:%s\\r\\n\", sendline);memset(recvline,0,MAXLINE);iResult = recv(s,recvline,MAXLINE,0);if (iResult > 0)printf(\"客户端接收到数据 %s \\r\\n\",recvline);else{if (iResult == 0)printf(\"服务器终止!\\n\");elseprintf(\"recv 函数调用错误,错误号:%d\\n\", WSAGetLastError());break;}memset(sendline, 0, MAXLINE);}return iResult;}//工作线程完成了在特定连接上接收客户端数据,并将数据发回的功能,直到客户端关闭连接或网络操作发生错误UINT tcp_server_fun_echo(LPVOID pParam){int iResult = 0;char recvline[MAXLINE];int err;//将输入参数转换为连接套接字SOCKET s = *((SOCKET *)pParam);do {memset(recvline, 0, MAXLINE);//接收数据iResult = recv(s, recvline, MAXLINE, 0);if (iResult > 0){printf(\"服务器接收到数据: %s\\n\",recvline);//回射发送已收到的数据iResult = send(s, recvline, MAXLINE, 0);if (iResult == SOCKET_ERROR){printf(\"send 函数调用错误,错误号:%d\\n\", WSAGetLastError());err = closesocket(s);if(err == SOCKET_ERROR)printf(\"closesocket 函数调用错误,错误号:%d\\n\", WSAGetLastError());iResult = -1;}elseprintf(\"服务器发送数据: %s\\n\", recvline);}else{if (iResult == 0)printf(\"对方连接关闭,退出\\n\");else{printf(\"recv 函数调用错误,错误号:%d\\n\", WSAGetLastError());iResult = -1;}err = closesocket(s);if (err == SOCKET_ERROR)printf(\"closesocket 函数调用错误,错误号:%d\\n\", WSAGetLastError());break;}} while (iResult > 0);return iResult;}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Socket基础三:基于流式套接字的网络程序(服务器回射程序设计)