作者:刘磊 2020.4.27
参考书目:《Windows网络编程》刘琰等著
-
回射程序
回射程序是进行网络诊断的常用工具之一,例如Ping是windows系统下自带的可执行命令,利用它可以检查网络是否连通,不过ping是网络层上的,不适合传输层的应用程序测试和诊断。
-
基本执行步骤
服务器:
- 引用头文件
- 创建流式套接字
- 捆绑服务器的指定端口到套接字
- 把套接字变换成监听套接字
- 接收客户连接
- 接收客户发来的数据
- 发送客户发来的数据
- 回到步骤6
- 如果客户端关闭连接,则终止当前连接
- 回到步骤5
- 如果终止条件到,则关闭套接字,释放资源,关闭程序。
客户机:
- 引用头文件
- 处理命令行参数
- 创建流式套接字
- 指定服务器IP地址和端口
- 与服务器建立连接
- 获得用户输入
- 发送回射请求
- 接收并输出服务器应答
- 回到步骤6
- 如果终止条件到,则关闭套接字,释放资源,终止程序
-
对回射功能进行编码
1)tcp_server_fun_echo(SOCKET s)编程代码如下:
//回射函数
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;
}
else
printf("对方连接关闭,退出\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)编程代码如下:
//回射函数
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");
else
printf("recv 函数调用错误,错误号:%d\n", WSAGetLastError());
break;
}
memset(sendline, 0, MAXLINE);
}
return iResult;
}
-
主函数
1、server.c
#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
#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
#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
#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;
}
else
printf("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_INET
memset( 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;
}
else
sap->sin_addr.S_un.S_addr = ulAddr;
}
else
sap->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;
}
//设置服务器位监听状态,监听队列长度为LISTENQ
iResult = 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;
}
//设置服务器位监听状态,监听队列长度为LISTENQ
iResult = 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;
}
else
printf("对方连接关闭,退出\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");
else
printf("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;
}
else
printf("服务器发送数据: %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;
}