之前我们使用的都是默认数据通信手段read&write函数及系统I/O函数,未使用过标准I/O函数。
标准函数都具有良好的移植性,而使用标准I/O函数会得到额外的缓冲支持。套接字缓冲和标准I/O缓冲关系如下图
假如我们通过fputs函数传输字符串“Hello”时,首先将数据传递到标准I/O函数的缓冲,然后数据将移动到套接字输出缓冲,最后将字符串发送到对方主机。其中套接字中的缓冲主要用于实现TCP的协议(防止数据丢失,在确认前保存的数据);而标准I/O函数缓冲的目的是提高性能。假如把一个字节的数据发送十次,由于发送的每个数据包存在固定大小的首部,传递的总字节数远远多于拥有缓冲后传递一个十字节的数据。
创建套接字时返回文件描述符,而为了使用标准I/O函数,只能将其转换为FILE结构体指针。
#include
FILE * fdopen(int fildes, const char * mode);
mode | 含义 |
---|---|
r | 打开只读文件,该文件必须存在 |
r+ | 打开可读写的文件,该文件必须存在 |
w | 打开只写文件,若文件不存在则建立该文件,存在则清零 |
w+ | 打开可读写文件,若文件不存在则建立该文件 ,存在则清零 |
a | 以附加的方式打开只写文件,若文件不存在,则会建立该文件 |
a+ | 以附加方式打开可读写的文件,若文件不存在,则会建立该文件 |
#include
#include
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");
fputs("Network C programming \n", fp);
fclose(fp);
return 0;
}
将Network C programming写入新建的data.dat中
与fdopen函数功能相反的函数fileno,它将FILE指针转换为文件描述符的函数
#include
int fileno(FILE *stream);
//todes.c
#include
#include
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 descriptor: %d \n", fd);
fp = fdopen(fd, "w");
fputs("TCP/IP SOCKET PROGRAMMING \n", fp);
printf("Second file descriptor: %d \n", fileno(fp));
fclose(fp);
return 0;
}
将data.dat的文件描述符转为FILE*,并改写内容为TCP/IP SOCKET PROGRAMMING,再将结构体指针转回文件描述符。输出转换前与转换后的文件描述符,都是3确定已经正确转换。
将 实现一个简单TCP服务器端与客户端 中的read、write函数用本节的标准I/O函数替代
//服务器端
//之前
while((str_len = read(clnt_sock,message,BUF_SIZE)) != 0)
write(clnt_sock,message,str_len);
//现在
readfp = fdopen(clnt_sock, "r");
writefp = fdopen(clnt_sock, "w");
while (!feof(readfp))
{
fgets(message, BUF_SIZE, readfp);
fputs(message, writefp);
fflush(writefp);//fflush函数立即将数据传输到客户端
}
fclose(readfp);
fclose(writefp);
完整的服务器端
//echo_stdserv.c
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock, clnt_sock;
char message[BUF_SIZE];
int str_len, i;
struct sockaddr_in serv_adr;
struct sockaddr_in clnt_adr;
socklen_t clnt_adr_sz;
FILE *readfp;
FILE *writefp;
if (argc != 2) {
printf("Usage : %s \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++)
{
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;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
客户端对比
//客户端
//之前
while(1)
{
...
write(sock,message,strlen(message));
str_len = read(sock,message,BUF_SIZE - 1); //去掉字符串结束符
message[str_len] = 0; //字符串结束符位置覆盖掉
...
}
//现在
readfp = fdopen(sock, "r");
writefp = fdopen(sock, "w");
while (1)
{
...
fputs(message, writefp);
fflush(writefp);
fgets(message, BUF_SIZE, readfp);
...
}
fclose(writefp);
fclose(readfp);
完整客户端代码如下;
//echo_stdclnt.c
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 1024
void error_handling(char *message);
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 \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"))
break;
fputs(message, writefp);
fflush(writefp);
fgets(message, BUF_SIZE, readfp);
printf("Message from server: %s", message);
}
fclose(writefp);
fclose(readfp);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}