1. 程式人生 > >TCP/IP網路程式設計 學習筆記_8 --優雅地斷開套接字連線

TCP/IP網路程式設計 學習筆記_8 --優雅地斷開套接字連線

基於TCP的半關閉

TCP中的斷開連線過程比建立連線過程更重要,因為建立連線過程一般不會出現什麼大的變數,但斷開過程就有可能發生預想不到的情況,因此要準確的掌控。

  • 單方面斷開連線帶來的問題
    Linux的close函式和Windows的closesocket函式是完全斷開連線。完全斷開是指無法傳輸資料也不能接收資料。因此,一方這樣直接斷開連線就顯得不太優雅了。如:主機A傳送完最後的資料後,呼叫close函式單方斷開了連線,那麼最終,由主機B傳輸的,主機A必須接收的確認資料也銷燬了(四次握手)。
    為了解決這類問題,我們一般採用半關閉的方法,這是指可以傳輸資料但無法接收,或可以接收資料但無法傳輸。就是隻關閉流的一半。

  • 套接字和流(Stream)
    兩臺主機通過套接字建立連線後進入可交換資料的狀態,我們把這種狀態看作一種流。如流水一樣,水朝一個方向流動,同樣,在套接字的流中,資料也只能向一個方向移動。示例圖如下:
    這裡寫圖片描述
    一旦兩臺主機建立了套接字連線,每個主機就會擁有單獨的輸入流和輸出流。如圖,其中一個主機的輸入流與另一主機的輸出流相連,而輸出流則與另一主機的輸入流相連。我們這章講的優雅斷開連線其實就是斷開其中1個流,而非同時斷開兩個流。

  • 針對優雅斷開的shutdown函式

int shutdown(int sock, int howto);
sock:需要斷開的套接字檔案描述符
howto:斷開連線的方式,有三種:SHUT_RD:斷開輸入流,SHUT_WR:斷開輸出流,SHUT_RDWR:同時斷開

基於半關閉的檔案傳輸程式

//
//  main.cpp
//  hello_server
//
//  Created by app05 on 15-8-10.
//  Copyright (c) 2015年 app05. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(char *message); int main(int argc, const char * argv[]) { int serv_sd, clnt_sd; FILE *fp; char buf[BUF_SIZE]; int read_cnt; struct sockaddr_in serv_adr, clnt_adr; socklen_t clnt_adr_sz; if (argc != 2) { printf("Usage: %s <port> \n", argv[0]); exit(1); } fp = fopen("/Users/app05/Desktop/server-clint/hello_server/hello_server/main.cpp", "rb"); serv_sd = socket(PF_INET, SOCK_STREAM, 0); 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])); bind(serv_sd, (struct sockaddr *)&serv_adr, sizeof(serv_adr)); listen(serv_sd, 5); clnt_adr_sz = sizeof(clnt_adr); clnt_sd = accept(serv_sd, (struct sockaddr *)&clnt_adr, &clnt_adr_sz); while (1) { read_cnt = fread((void *)buf, 1, BUF_SIZE, fp); if (read_cnt < BUF_SIZE) { write(clnt_sd, buf, read_cnt); break; } write(clnt_sd, buf, BUF_SIZE); } /*半關閉就是指一方關閉時,先只關閉輸出流,輸入流需要在收到另一方確認斷開資訊後才能關閉*/ shutdown(clnt_sd, SHUT_WR);//關閉輸出流 read(clnt_sd, buf, BUF_SIZE); printf("Message from client: %s \n", buf); fclose(fp); close(clnt_sd); close(serv_sd); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
//
//  main.cpp
//  hello_client
//
//  Created by app05 on 15-8-10.
//  Copyright (c) 2015年 app05. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(char *message);


int main(int argc, const char * argv[]) {
    int sd;
    FILE *fp;

    char buf[BUF_SIZE];
    int read_cnt;
    struct sockaddr_in serv_adr;
    if (argc != 3) {
        printf("Usage: %s <IP> <port> \n",argv[0]);
        exit(1);
    }

    fp = fopen("/Users/app05/Desktop/server-clint/hello_client/hello_client/receive.txt", "wb");
    sd = socket(PF_INET, SOCK_STREAM, 0);

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

    connect(sd, (struct sockaddr *)&serv_adr, sizeof(serv_adr));

    /*read成功時返回接收的位元組數(但遇到檔案結尾則返回0),失敗時返回-1*/
    while ((read_cnt = read(sd, buf, BUF_SIZE)) != 0 )
        fwrite((void *)buf, 1, read_cnt, fp);

    puts("Received file data");
    write(sd, "Thank you", 10); //回覆可以斷開資訊

    fclose(fp);
    close(sd);
    return 0;
}


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

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述