首页 > 其他 > 详细

shutdown函数

时间:2014-04-19 02:41:48      阅读:517      评论:0      收藏:0      [点我收藏+]

发送一行文本给服务器,然后等待应答,这就形成了一个简单的回射服务器。如果把客户和服务器之间的网络作为全双工管道考虑,请求从客户向服务器发送,应答从服务器回发给客户。下图展示了客户与服务器之间的数据分组,忽略网络中的TCP确认(ACK)。


bubuko.com,布布扣


下面是一个批量发送一行数据的示例程序,将标准输入重定向到一个拥有600多行字符的文件,将服务器发送回来的输出重定向到一个用来接收的文件。对于回射服务器而言,它们理应相等。


头文件

#ifndef MY_NET_H
#define MY_NET_H

#include <sys/types.h>      
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>

#define MAXLINE 4096
#define SA struct sockaddr
#define LISTENEQ 10

/*err_quit*/
void err_quit(const char* err_string)
{
	printf("%s\n", err_string);
	exit(-1);
}

/*err_sys*/
void err_sys(const char* err_string)
{
	perror(err_string);
	exit(-1);
}

/*Socket*/
int Socket(int domain, int type, int protocol)
{
	int sockfd = socket(domain, type, protocol);
	if (sockfd == -1)
		err_sys("socket error");
	return sockfd;
}

/*Inet_pton*/
int Inet_pton(int af, const char *src, void *dst)
{
	int r;
	if ((r = inet_pton(af, src, dst)) <= 0)
		err_sys("inet_pton error");
	return r;
}

/*Connect*/
int Connect(int sockfd, 
			const struct sockaddr *addr,
            socklen_t addrlen)
{
	int r;
	if ((r = connect(sockfd, addr, addrlen)) == -1)
		err_sys("connect error");
	return r;
}

/*Bind*/
int Bind(int sockfd, 
		const struct sockaddr *addr,
        socklen_t addrlen)
{
	int r;
	r = bind(sockfd, addr, addrlen);
	if (r == -1)
		err_sys("bind error");
	return r;
}

/*Listen*/
int Listen(int sockfd, int backlog)
{
	int r;
	r = listen(sockfd, backlog);
	if (r == -1)
		err_sys("listen error");
	return r;
}

/*Accept*/
int Accept(int sockfd, 
		   struct sockaddr *addr, 
		   socklen_t *addrlen)
{
	int r;
	r = accept(sockfd, addr, addrlen);
	if (r == -1)
		err_sys("accept error");
	return r;
}

/*Close*/
int Close(int fd)
{
	int r = close(fd);
	if (r == -1)
		err_sys("close error");
	return r;
}

/*Read*/
int Read(int fd, void *buf, size_t count)
{
	int r;
	r = read(fd, buf, count);
	if (r == -1)
		err_sys("read error");
	return r;
}

/*Write*/
int Write(int fd, const void *buf, size_t count)
{
	int r;
	r = write(fd, buf, count);
	if (r == -1)
		err_sys("write error");
	return r;
}

/*Writen*/
//返回剩余的字符
int Writen(int fd, const void *buf, int len)
{
	int r;
	const char* ptr;
	int nleft = len;
	
	if(len <= 0)
		return -1;
	
	ptr = buf;
	while (1)
	{
		r = Write(fd, ptr, len);
		nleft -= r;
		ptr += r;
		len -= r;

		if (nleft == 0)
			return 0;
	}	
}

/*Readn*/
int Readn(int fd, void *buf, int len)
{
	int r;
	char* ptr;
	
	ptr = buf;
	while (1)
	{
		r = Read(fd, ptr, len);
		if (r == 0)
			return 0;
		if (len-r > 0)
		{
			len -= r;
			ptr += r;
		}
		else if (len-r == 0)
			return len;
	}
}
#endif


客户程序

#include "net.h"

main()
{
	int    sockfd, r, len;
	char   recvline[MAXLINE + 1];
	struct sockaddr_in servaddr;
	fd_set rset;
	int    maxfd, nfds;
		
	sockfd = Socket(AF_INET, SOCK_STREAM, 0);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(22222);
	Inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
	
	Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));
	
	maxfd = sockfd;
	nfds = maxfd + 1;
	FD_ZERO(&rset);

	while (1)
	{
		FD_SET(fileno(stdin), &rset);
		FD_SET(sockfd, &rset);
		
		r = select(nfds, &rset, NULL,
				   NULL, NULL);
		if (r == -1)
		{
			//perror("select error");
			exit(-1);
		}
		
		//标注输入可读
		if (FD_ISSET(fileno(stdin), &rset))
		{
			if(gets(recvline) != NULL)
			{
				len = strlen(recvline);
				r = Writen(sockfd, recvline, len);
			}
			else
			{
				Close(sockfd);    //读到文件末尾关闭套接字,此时可能仍有数据在管道中
			}
		}
		
		//套接字描述符可读
		if (FD_ISSET(sockfd, &rset))
		{
			r = Readn(sockfd, recvline, len);
			if (r == 0)
			{
				break;
			}
			recvline[r] = 0;
			printf("%s\n", recvline);
		}
	}
	
	exit(0);
}

服务器程序

#include "net.h"

main(int argc, char **argv)
{
	int listenfd, connfd, r;
	struct sockaddr_in servaddr;
	char buf[MAXLINE];
	time_t ticks;
	
	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(22222);
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
	
	Listen(listenfd, LISTENEQ);
	
	connfd = accept(listenfd, (SA*)NULL, NULL);
	
	while(1)
	{
		r = read(connfd, buf, MAXLINE);
		if (r == 0)
		{
			printf("客户端断开连接\n");
			break;
		}
		buf[r] = 0;
		Writen(connfd, buf, r);
	}
	
	exit(0);
}

重定向文件
./c <cdx_input.txt >cdx_output.txt

执行程序后查看用来接收服务器发送回数据的文件

bubuko.com,布布扣


bubuko.com,布布扣


bubuko.com,布布扣


如图所示,运行几次后,却发现接收文件总是小于发送文件。这是为什么呢?
问题在于gets读到文件末尾会跳出循环调用close,而此时虽然数据数据发送完毕,但管道中仍有可能还有去往服务器的数据,或从服务器返回客户的数据,此时close会终止读和写两个方向的数据传送,导致接收到的数据小于发送的数据。如下图所示。


bubuko.com,布布扣

close与shutdown区别

1.close终止读、写两个方向的数据发送.
2.close把描述符引用计数-1,仅在计数变为0时才关闭套接字。而shutdown可以不管引用计数就激发终止序列(FIN)。


原型:

#include <sys/socket.h>
int shutdown(int sockfd, int how);
					返回值:成功返回0,出错返回-1

参数:
how有三个值
SHUT_RD:关闭读,套接字中不再接收数据,接收缓冲区中现有数据被丢弃。进程不能对该套接字再调用任何读函数,由该套接字接收的来自对端的数据被确认,然后被丢弃。
SHUT_WR:关闭写,对于TCP套接字称为半关闭,当先缓冲区数据被发送掉,后跟TCP正常终止序列,进程不能对该套接字再调用任何写函数。
SHUT_RDWR:相当于先调用两次shutdown,第一次指定SHUT_RD,第二次指定SHUT_WR。


修改后的客户程序,同时通知服务器发送数据完毕,而且可以继续接收数据。

#include "net.h"

main()
{
	int    sockfd, r, len;
	char   recvline[MAXLINE + 1];
	struct sockaddr_in servaddr;
	fd_set rset;
	int    maxfd, nfds;
		
	sockfd = Socket(AF_INET, SOCK_STREAM, 0);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(22222);
	Inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
	
	Connect(sockfd, (SA*)&servaddr, sizeof(servaddr));
	//printf("连接成功\n");
	
	maxfd = sockfd;
	nfds = maxfd + 1;
	FD_ZERO(&rset);

	while (1)
	{
		FD_SET(fileno(stdin), &rset);
		FD_SET(sockfd, &rset);
		
		r = select(nfds, &rset, NULL,
				   NULL, NULL);
		if (r == -1)
		{
			//perror("select error");
			exit(-1);
		}
		
		//标注输入可读
		if (FD_ISSET(fileno(stdin), &rset))
		{
			if(gets(recvline) != NULL)
			{
				len = strlen(recvline);
				r = Writen(sockfd, recvline, len);
			}
			else
			{
				shutdown(sockfd, SHUT_WR);
			}
		}
		
		//套接字描述符可读
		if (FD_ISSET(sockfd, &rset))
		{
			r = Readn(sockfd, recvline, len);
			if (r == 0)
			{
				break;
			}
			recvline[r] = 0;
			printf("%s\n", recvline);
		}
	}
	
	exit(0);
}

执行结果

bubuko.com,布布扣


接收文件

bubuko.com,布布扣






shutdown函数,布布扣,bubuko.com

shutdown函数

原文:http://blog.csdn.net/aspnet_lyc/article/details/24045867

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!