c++ 網絡編程(七)TCP/IP LINUX下 socket編程 基於套接字的標準I/O函數使用 與 fopen,feof,fgets,fputs函數用法

原文作者:aircraft

原文鏈接:https://www.cnblogs.com/DOMLX/p/9614820.html

一.標準I/O

1,什麼是標準I/O?其實是指C語言裏的文件操作函數,如:fopen,feof,fgetc,fputs等函數,他們和平臺無關。

2,網絡通信中使用標準I/O的優點:

  • 良好的移植性。良好移植性這個不需多解釋,不僅是I/O函數,所有的標準函數都具有良好的移植性。因爲,爲了支持所有的操作系統(編譯器),這些函數都是按照ANSI C標準定義的。
  • 標準I/O函數可以利用緩衝提高性能。在網絡通信中,read,write傳輸數據只有一種套接字緩衝,但使用標準I/O傳輸會有額外的緩衝,即I/O緩衝和套接字緩衝兩個。使用I/O緩衝主要是爲了提高性能,需要傳輸的數據越多時越明顯。因爲,一次發送更多的數據要比分多次發送同樣的數據性能要高。發送一次數據就對應一個數據包,往往數據包的頭信息比較大,它與數據大小無關。

3,網絡通信中使用標準I/O的缺點:

  • 不容易進行雙向通信。
  • 有時可能頻繁調用fflush函數。
  • 需要以FILE結構體指針的形式返回文件描述符。

4,轉換函數

//將文件描述符轉換爲標準I/O函數中使用的FILE結構體指針 FILE * fdopen(int fildes, const char *mode); 成功時返回轉換的FILE結構體指針,失敗返回NULL //將FILE結構體指針轉換爲文件描述符 int fileno(FILE *stream); 成功返回轉換後的文件描述符,失敗返回-1

註釋:套接字中使用標準I/O,其實主要是運用在需要傳輸大量數據的情況,因爲其需要編寫額外代碼,所以並不像想象中的那麼常用。

先給個fdopen函數的簡單示例:

#include <stdio.h>
main()
{
    FILE * fp = fdopen(0, "w+");
    fprintf(fp, "%s\n", "hello!");
    fclose(fp);
}

fileno示例:

#include <stdio.h>
main()
{
     FILE   *fp;
     int   fd;
     fp = fopen("/etc/passwd", "r");
     fd = fileno(fp);
     printf("fd = %d\n", fd);
     fclose(fp);
}

5.fgets與fputs函數:

1.fgets()

功能:有文件中讀取一字符串

定義:char *fgets(char *s, int size, FILE *stream) 說明: fgets()用來從參數stream所指的文件讀入字符並存到參數s所指向的內存空間,

直到讀到換行字符\n,讀到文件尾或是讀到size-1個字符爲止,最後會加入NULL作爲文件結束。

返回值: 成功 返回s的指針 失敗 返回NULL

2.fputs()

功能:將一指定的字符串寫入文件內

定義: char * fputs(const *char s, FILE *stream) 說明: fputs()用來將s所指的字符串寫到參數stream所指向的文件中 返回值: 成功 返回寫入字符串的個數 失敗 返回EOF

示例代碼:

include <stdio.h>
 
int main()
{
int str[100];
fputs(fgets(str, 100, strin), strout);
 
return 0;
}

6.feof介紹:

1.在stdio.h中的宏定義

#define  _IOEOF  0x0010  #define  feof(_stream)  ((_stream)->_flag & _IOEOF)

2.feof的使用:

feof用檢測流上的文件結束符,其返回值有兩種情況:如果遇到文件結束,函數值爲非零值,否則函數值爲0。

注:此處的文件結束標誌是EOF,EOF的16進制代碼爲0xFF(十進制爲-1),特用在文本文件中,因爲在文本文件中數據是以ASCⅡ代碼值的形式存放,普通字符的ASCⅡ代碼的範圍是32到127(十進制),與EOF不衝突,因此可以直接使用。但是在二進制文件中,數據有可能出現-1,因此不能用EOF來作爲二進制文件的結束標誌,可以通過feof函數來判斷。

注意了這些標準I/O函數速度是比平常的函數快很多很多的,不過也不是每次都用到,具體看對什麼情況了

二.基於標準I/O函數實現套接字服務端與客戶端通信

LINUX下服務端:

#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);

int main(int argc, const 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++) {
        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);

        //將文件描述符轉換爲FILE結構體指針
        readfp = fdopen(clnt_sock, "r");
        writefp = fdopen(clnt_sock, "w");
        while (!feof(readfp))
        {
            //轉化爲標準I/O操作
            fgets(message, BUF_SIZE, readfp);//相當於read,接收
            fputs(message, writefp); //相當於write,發送
            fflush(writefp); //刷新緩衝,立即顯示而不是一直放緩衝中,保證立即將數據傳輸到客服端
        }
        fclose(readfp);
        fclose(writefp);
    }

    close(serv_sock);
    return 0;
}

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

LINUX下客戶端:

#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);

int main(int argc, const 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"))
            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);
}

最後說一句啦。本網絡編程入門系列博客是連載學習的,有興趣的可以看我博客其他篇。。。。

好了今天對網絡編程的學習就到這裏結束了,小飛機我要撤了去吃飯了。,,,很多人大學都很迷茫不知道學點什麼好,,,,,管他的,想那麼多幹嘛,先學了再說,對技術如有偏見,那麼你的領域就侷限於此了---《一專多精》

參考博客:https://blog.csdn.net/u010223072/article/details/48316117

參考博客:https://blog.csdn.net/qq_32103869/article/details/50834629

參考書籍:《TCP/IP 網絡編程 --尹聖雨》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章