网络编程七--半关闭

文章介绍了在TCP通信中,当主机A使用closesocket完全断开连接后,无法再接收主机B的数据。为解决此问题,引入了shutdown函数实现半关闭,即可以关闭套接字的输入或输出流,保持单向通信。文中给出了半关闭服务器和客户端的示例代码,展示如何使用shutdown进行半关闭操作,使得在服务器关闭输出流后仍能接收客户端的数据。
摘要由CSDN通过智能技术生成

写在前面

此前的closesocket函数,调用后就会完全断开连接。完全断开后,不仅无法传输数据,也无法再接收数据。在某些场景下,完全断开连接可能会有些问题。

若有两台主机正在进行双向通信。主机A发送完最后的数据后,调用closesocket函数断开连接(这里会将该套接字的输出缓存的数据全部发送出去),之后主机A无法再接收主机B正在传输的数据(实际上是完全无法调用与接收数据相关的函数)。

为了解决这类问题,“只关闭一部分数据交换中使用的流(Half-close)”的方法应运而生。断开一部分连接是指,可以传输数据但无法接收,或者可以接收数据但无法传输,及关闭IO流的一半。

shutdown函数

一旦两台主机间建立了套接字连接,每个主机(套接字)都会拥有单独的输入流和输出流,即其中一个主机的输入流和另一个主机的输入流相连,而输出流则和另一主机的输入流相连,以此进行全双工通信。使用shutdown函数可以只断开指定流,而不是全关闭。

函数原型如下:

#include <winsock2.h>

int shutdown(SOCKET sock, int howto);
//返回值:成功返回0,失败返回SOCKET_ERROR
//sock: 要断开的套接字句柄
//howto:断开方式信息
//howto参数可选项:
//SD_RECEIVE: 断开输入流
//SD_SEND: 断开输出流
//SD_BOTH: 同时断开输入输出流

下面给出简单的半关闭服务器/客户端示例。

半关闭服务器

// HalfClose_Server.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE 30

/*
Windows半关闭接口
int shutdow(SOCKET sock, int howto);
sock: 要半关闭的套接字句柄
howto: 断开方式的信息.可选:SD_RECEIVE -- 断开输入流,SD_SEND -- 断开输出流,SD_BOTH -- 同时断开IO流
返回值:成功时返回0,失败时返回SOCKET_ERROR
*/

int _tmain(int argc, _TCHAR* argv[])
{
	if (argc != 2)
	{
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("WSAStartup Error!");
		return -1;
	}

	SOCKET srvSock = socket(PF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == srvSock)
	{
		printf("socket error!");
		WSACleanup();
		return -1;
	}

	SOCKADDR_IN srvAddr;
	memset(&srvAddr, 0, sizeof(srvAddr));
	srvAddr.sin_family = PF_INET;
	srvAddr.sin_addr.s_addr = htonl(ADDR_ANY);
	srvAddr.sin_port = htons(_ttoi(argv[1]));

	if (SOCKET_ERROR == bind(srvSock, (sockaddr*)&srvAddr, sizeof(srvAddr)))
	{
		printf("bind error!");
		closesocket(srvSock);
		WSACleanup();
		return -1;
	}

	if (SOCKET_ERROR == listen(srvSock, 5))
	{
		printf("listen error!");
		closesocket(srvSock);
		WSACleanup();
		return -1;
	}

	SOCKADDR_IN cltAddr;
	memset(&cltAddr, 0, sizeof(cltAddr));
	int nCltAddrSize = sizeof(cltAddr);

	SOCKET cltSock = accept(srvSock, (sockaddr*)&cltAddr, &nCltAddrSize);
	if (INVALID_SOCKET == cltSock)
	{
		printf("accept error!");
		closesocket(srvSock);
		WSACleanup();
		return -1;
	}

	int nRecvLen = 0;
	char Msg[BUF_SIZE] = "0123456789";
	while(true)
	{
		nRecvLen = strlen(Msg);
		printf("接收长度: %d\n", nRecvLen);

		if (nRecvLen < BUF_SIZE)
		{
			printf("break\n");
			send(cltSock, Msg, nRecvLen, 0);
			break;
		}

		send(cltSock, Msg, BUF_SIZE, 0);
	}

	//半关闭:输出流
	shutdown(cltSock, SD_SEND);

	nRecvLen = recv(cltSock, Msg, BUF_SIZE, 0);
	printf("Msg From Client: %s\n", Msg);

	closesocket(srvSock);
	WSACleanup();

	fputs("任意键结束...", stdout);
	getchar();
	return 0;
}

半关闭客户端

// HalfClose_Client.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

#define BUF_SIZE 30


int _tmain(int argc, _TCHAR* argv[])
{
	if(argc != 3)
	{
		printf("argc error!\n");
		return -1;
	}

	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("WSAStartup error!\n");
		return -1;
	}

	SOCKET srvSock = socket(PF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == srvSock)
	{
		printf("socket error!\n");
		WSACleanup();
		return -1;
	}

	SOCKADDR_IN srvAddr;
	memset(&srvAddr, 0, sizeof(srvAddr));
	srvAddr.sin_family = PF_INET;
	srvAddr.sin_addr.s_addr = inet_addr(argv[1]);
	srvAddr.sin_port = htons(_ttoi(argv[2]));

	if (SOCKET_ERROR == connect(srvSock, (sockaddr*)&srvAddr, sizeof(srvAddr)))
	{
		printf("connect error!\n");
		closesocket(srvSock);
		WSACleanup();
		return -1;
	}

	int nRecvLen = 0;
	char Msg[BUF_SIZE];
	while ( (nRecvLen = recv(srvSock, Msg, BUF_SIZE, 0)) != 0 )
	{
		Msg[nRecvLen] = 0;
		printf("Recv From Server: %s\n", Msg);
	}

	//服务器器半关闭后
	send(srvSock, "thank you", 10, 0);
	closesocket(srvSock);
	WSACleanup();

	fputs("任意键结束...", stdout);
	getchar();

	return 0;
}

运行结果如图:
半关闭测试结果
可以看到服务器半关闭输出流后,还能接收到客户端(输入流)的消息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值