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)编程代码如下:

//回射函数
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;
}









 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值