当前位置:   article > 正文

llinux C语言在网络编程时使用标准IO

llinux C语言在网络编程时使用标准IO

目录

fdopen(3)函数

使用标准IO发送数据

使用标准IO接收数据

注意


先看一个简单的TCP客户端程序:

  1. #include <stdio.h>
  2. #include <arpa/inet.h>
  3. #include <sys/socket.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <errno.h>
  7. #include <unistd.h>
  8. int main(void){
  9. int fd = socket(AF_INET, SOCK_STREAM, 0);
  10. if(fd == -1){
  11. perror("socket");
  12. exit(1);
  13. }
  14. struct sockaddr_in addr;
  15. addr.sin_family = AF_INET;
  16. addr.sin_port = htons(8000);
  17. addr.sin_addr.s_addr = inet_addr("192.168.1.14");
  18. if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1){
  19. perror("connect");
  20. exit(1);
  21. }
  22. // 准备写入
  23. char message[] = "Hello World!";
  24. char *buf = message;
  25. int ret, len;
  26. len = strlen(message);
  27. while(len != 0 && (ret = write(fd, buf, len)) != 0){
  28. if(ret == -1){
  29. if(errno == EINTR) continue; // 写入时收到信号,可以重新写入
  30. perror("write");
  31. break;
  32. }
  33. len -= ret;
  34. buf += ret;
  35. }
  36. close(fd);
  37. return 0;
  38. }

可以看到,在发送信息时,使用了这样的代码:

  1. char message[] = "Hello World!";
  2. char *buf = message;
  3. int ret, len;
  4. len = strlen(message);
  5. while(len != 0 && (ret = write(fd, buf, len)) != 0){
  6. if(ret == -1){
  7. if(errno == EINTR) continue; // 写入时收到信号,可以重新写入
  8. perror("write");
  9. break;
  10. }
  11. len -= ret;
  12. buf += ret;
  13. }

这是因为,使用write(2)函数时,可能会出现写入不全的情况(部分写),因此,需要通过while循环,确保它能全部写完。

但是,这样的代码太麻烦了,有没有简单的办法? 

fdopen(3)函数

我们知道,在标准IO中,有一个fdopen(3)函数:

  1. #include <stdio.h>
  2. FILE *fdopen(int fd, const char *mode);

这个函数将会把一个文件描述符转换成一个文件指针。参数中,fd是文件描述符,mode是操作模式,如"r+"。可以设置为"w"或"w+",但不会清空文件。如果把socket(2)函数返回的文件描述符改成文件指针,就可以通过fprintf等方法直接输出了。

使用标准IO发送数据

我们可以修改代码如下:

  1. #include <stdio.h>
  2. #include <arpa/inet.h>
  3. #include <sys/socket.h>
  4. #include <stdlib.h>
  5. int main(void){
  6. int fd = socket(AF_INET, SOCK_STREAM, 0);
  7. if(fd == -1){
  8. perror("socket");
  9. exit(1);
  10. }
  11. struct sockaddr_in addr;
  12. addr.sin_family = AF_INET;
  13. addr.sin_port = htons(8000);
  14. addr.sin_addr.s_addr = inet_addr("192.168.1.14");
  15. if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1){
  16. perror("connect");
  17. exit(1);
  18. }
  19. FILE *fp = fdopen(fd, "r+");
  20. fprintf(fp, "Hello World!");
  21. fclose(fp);
  22. return 0;
  23. }

可以看到,采用标准IO,代码就简单了很多。而且,使用标准IO,也可以避免操作底层的时候出现一些问题(如部分写)

我们可以用网络调试助手看一下使用了这种方法是否可以正常发送:

可以看到,它能够正常发送数据,并正常关闭套接字。

使用标准IO接收数据

我们还可以让它能够读取数据:

  1. #include <stdio.h>
  2. #include <arpa/inet.h>
  3. #include <sys/socket.h>
  4. #include <stdlib.h>
  5. int main(void){
  6. int fd = socket(AF_INET, SOCK_STREAM, 0);
  7. if(fd == -1){
  8. perror("socket");
  9. exit(1);
  10. }
  11. struct sockaddr_in addr;
  12. addr.sin_family = AF_INET;
  13. addr.sin_port = htons(8000);
  14. addr.sin_addr.s_addr = inet_addr("192.168.1.14");
  15. if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1){
  16. perror("connect");
  17. exit(1);
  18. }
  19. FILE *fp = fdopen(fd, "r+");
  20. char buf[1024];
  21. fgets(buf, 1024, fp);
  22. printf("data is %s\n", buf);
  23. fclose(fp);
  24. return 0;
  25. }

 使用网络调试助手发送数据(注意要在数据后面加入换行符):

终端输出:

data is Hello Client.

注意

标准IO的设计主要是为了文件读写,将其应用与网络编程并不总是最佳选择。另外需要注意,使用了fdopen(3)函数后,不应直接操作文件描述符(虽然这是合法的),这可能会导致数据竞争等未定义行为。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号