TCP/IP网络编程 (十五):套接字和标准I/O (fdopen & fileno函数)

标准I/O的优点

标准I/O函数的两个优点

--标准I/O函数具有良好的移植性

--标准I/O函数可以利用缓冲提高性能

                                                                            

 

由上图可看到:使用标准I/O函数传输数据时,经过2个缓冲。例如:通过fputs函数传输字符串时,首先字符串传递到标准I/O函数的输出缓冲,然后数据将移动到套接字输出缓冲,最后将字符串发送到对方主机。

 

 

标准I/O函数与系统函数之间的性能对比

分别利用标准I/O函数和系统函数编写文件复制程序,主要为了检验缓冲提高性能的程度。

 

利用系统函数复制文件的示例:

#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#define BUF_SIZE 3		//用最短数组长度构成

int main(int argc,char *argv[])
{
	int fd1,fd2;		//保存在fd1和fd2中的是文件描述符!
	int len;
	char buf[BUF_SIZE];

	fd1 = open("news.txt",O_RDONLY);
	fd2 = open("cpy.txt",O_WRONLY | O_CREAT | O_TRUNC);

	while ((len = read(fd1,buf,sizeof(buf))) > 0)
		write(fd2,buf,len);

	close(fd1);
	close(fd2);
	return 0;
}


采用标准I/O函数复制文件示例:

#include<stdio.h>
#define BUF_SIZE 3				//用最短数组长度构成

int main(int argc,char *argv[])
{
	FILE * fp1;		//保存在fp1中的是FILE结构体指针
	FILE * fp2;		//....
	char buf[BUF_SIZE];

	fp1 = fopen("news.txt","r");
	fp2 = fopen("cpy.txt","w");

	while(fgets(buf,BUF_SIZE,fp1) != NULL)
		fputs(buf,fp2);

	fclose(fp1);
	fclose(fp2);
	return 0;
}

fgets和fputs是一种基于缓冲的复制。

 

标准I/O函数的几个缺点

--不容易进行双向通信

--有时可能频繁调用fflush函数

--需要以FILE结构体指针的形式返回文件描述符

 

使用标准I/O函数

创建套接字时返回文件描述符,为了使用标准I/O函数,只能将其转换为FILE结构体指针。

 

利用fdopen函数转换为FILE结构体指针

 

 

示例:desto.c

#include<stdio.h>
#include<fcntl.h>

int main(void)
{
	FILE *fp;
	int fd = open("data.dat", O_WRONLY | O_CREAT | O_TRUNC);
	if (fd == -1)
	{
		fputs("file open error!",stdout);
		return -1;
	}

	fp = fdopen(fd,"w");		//将fd转换为FILE指针,向第二个参数传递w,因此返回写模式FILE指针
	fputs("Network C programming \n",fp);
	fclose(fp);
	return 0;
}

运行结果:

 

 

利用fileno函数转换为文件描述符

 

此函数与fdopen函数功能相反。

示例: 

#include<stdio.h>
#include<fcntl.h>

int main(void)
{
	FILE *fp;
	int fd = open("data.dat", O_WRONLY | O_CREAT | O_TRUNC);
	if (fd == -1)
	{
		fputs("file open error",stdout);
		return -1;
	}

	printf("First file description: %d \n",fd);			//输出文件描述符整数值
	fp = fdopen(fd,"w");						//转化为FILE指针
	fputs("TCP/IP SOCKET PROGRAMMING \n",fp);
	printf("Second file description: %d \n",fileno(fp));//转化为文件描述符,并输出
	fclose(fp);
	return 0;
}

运行结果:

 

基于套接字的标准I/O函数使用

将之前的回声服务器端和客户端改为基于标准I/O函数的数据交换形式。

 

服务器端:echo_stdserv.c

/* 回声服务器端echo_server.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#define BUF_SIZE 1024

void error_handling(char *message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

int main(int argc,char *argv[])
{
	int serv_sock, clnt_sock;
	char message[BUF_SIZE];

	int str_len, i;

	struct sockaddr_in serv_adr, clnt_adr;
	socklen_t clnt_adr_sz;
	FILE * readfp;
	FILE * writefp;

	if (argc != 2) {
		printf("Usage : %s <port> \n",argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET,SOCK_STREAM,0);
	if (serv_sock == -1)
		error_handling("socket() error!");

	memset(&serv_adr,0,sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));

	if (bind(serv_sock,(struct sockaddr*) &serv_adr,sizeof(serv_adr)) == -1)
		error_handling("bind() error!");

	if (listen(serv_sock,5) == -1)
		error_handling("listen() error!");
	
	clnt_adr_sz = sizeof(clnt_adr);

	for (i = 0; i < 5; i++)				//迭代5次
	{
		clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);
		if (clnt_sock == -1)
			error_handling("accept() error!");
		else 
			printf("Connected client %d\n",i+1);

		readfp  = fdopen(clnt_sock,"r");
		writefp = fdopen(clnt_sock,"w");
		while(!feof(readfp))
		{
			fgets(message,BUF_SIZE,readfp);
			fputs(message,writefp);
			fflush(writefp);			//冲洗缓冲
		}
		fclose(readfp);
		fclose(writefp);
	}
	close(serv_sock);
	return 0;
}

 

客户端:echo_client.c

/* 回声客户端 */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>

#define BUF_SIZE 1024

void error_handling(char *message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

int main(int argc,char *argv[])
{
	int sock;
	char message[BUF_SIZE];
	int str_len;
	struct sockaddr_in serv_adr;
	FILE * readfp;
	FILE * writefp;

	if(argc != 3) {
		printf("Usage : %s <IP> <port>\n",argv[0]);
		exit(1);
	}
	
	sock = socket(PF_INET,SOCK_STREAM,0);
	if (sock == -1)
		error_handling("socket() error!");

	memset(&serv_adr,0,sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
	serv_adr.sin_port = htons(atoi(argv[2]));

	if (connect(sock,(struct sockaddr*) &serv_adr,sizeof(serv_adr)) == -1)
		error_handling("connect() error!");
	else 
		puts("Connected.........");

	readfp = fdopen(sock,"r");
	writefp = fdopen(sock,"w");
	while (1) {
		fputs("Input message(Q to quit): ",stdout);
		fgets(message,BUF_SIZE,stdin);

		if (!strcmp(message,"q\n") || !strcmp(message,"Q\n"))		// 输入q/Q退出
			break;
		
		fputs(message,writefp);
		fflush(writefp);
		fgets(message,BUF_SIZE,readfp);
		printf("Message from server: %s",message);
	}

	fclose(writefp);
	fclose(readfp);
	return 0;
}

运行结果:

 

 

猜你喜欢

转载自blog.csdn.net/amoscykl/article/details/80303781