赞
踩
目录
先看一个简单的TCP客户端程序:
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <unistd.h>
-
- int main(void){
- int fd = socket(AF_INET, SOCK_STREAM, 0);
- if(fd == -1){
- perror("socket");
- exit(1);
- }
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(8000);
- addr.sin_addr.s_addr = inet_addr("192.168.1.14");
- if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1){
- perror("connect");
- exit(1);
- }
- // 准备写入
- char message[] = "Hello World!";
- char *buf = message;
- int ret, len;
- len = strlen(message);
- while(len != 0 && (ret = write(fd, buf, len)) != 0){
- if(ret == -1){
- if(errno == EINTR) continue; // 写入时收到信号,可以重新写入
- perror("write");
- break;
- }
- len -= ret;
- buf += ret;
- }
- close(fd);
- return 0;
- }
可以看到,在发送信息时,使用了这样的代码:
- char message[] = "Hello World!";
- char *buf = message;
- int ret, len;
- len = strlen(message);
- while(len != 0 && (ret = write(fd, buf, len)) != 0){
- if(ret == -1){
- if(errno == EINTR) continue; // 写入时收到信号,可以重新写入
- perror("write");
- break;
- }
- len -= ret;
- buf += ret;
- }
这是因为,使用write(2)函数时,可能会出现写入不全的情况(部分写),因此,需要通过while循环,确保它能全部写完。
但是,这样的代码太麻烦了,有没有简单的办法?
我们知道,在标准IO中,有一个fdopen(3)函数:
- #include <stdio.h>
- FILE *fdopen(int fd, const char *mode);
这个函数将会把一个文件描述符转换成一个文件指针。参数中,fd是文件描述符,mode是操作模式,如"r+"。可以设置为"w"或"w+",但不会清空文件。如果把socket(2)函数返回的文件描述符改成文件指针,就可以通过fprintf等方法直接输出了。
我们可以修改代码如下:
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #include <stdlib.h>
-
- int main(void){
- int fd = socket(AF_INET, SOCK_STREAM, 0);
- if(fd == -1){
- perror("socket");
- exit(1);
- }
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(8000);
- addr.sin_addr.s_addr = inet_addr("192.168.1.14");
- if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1){
- perror("connect");
- exit(1);
- }
- FILE *fp = fdopen(fd, "r+");
- fprintf(fp, "Hello World!");
- fclose(fp);
- return 0;
- }
可以看到,采用标准IO,代码就简单了很多。而且,使用标准IO,也可以避免操作底层的时候出现一些问题(如部分写)
我们可以用网络调试助手看一下使用了这种方法是否可以正常发送:
可以看到,它能够正常发送数据,并正常关闭套接字。
我们还可以让它能够读取数据:
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #include <stdlib.h>
-
- int main(void){
- int fd = socket(AF_INET, SOCK_STREAM, 0);
- if(fd == -1){
- perror("socket");
- exit(1);
- }
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(8000);
- addr.sin_addr.s_addr = inet_addr("192.168.1.14");
- if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1){
- perror("connect");
- exit(1);
- }
- FILE *fp = fdopen(fd, "r+");
- char buf[1024];
- fgets(buf, 1024, fp);
- printf("data is %s\n", buf);
- fclose(fp);
- return 0;
- }
使用网络调试助手发送数据(注意要在数据后面加入换行符):
终端输出:
data is Hello Client.
标准IO的设计主要是为了文件读写,将其应用与网络编程并不总是最佳选择。另外需要注意,使用了fdopen(3)函数后,不应直接操作文件描述符(虽然这是合法的),这可能会导致数据竞争等未定义行为。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。