天天看點

C語言網絡程式設計:close或者shutdown斷開通信連接配接

文章目錄

  • ​​前言​​
  • ​​close函數介紹​​
  • ​​shutdown函數介紹​​

前言

這裡在主要通過執行個體進行描述close函數在網絡程式設計中的使用

TCP程式設計模型中用戶端或者伺服器隻要主動通過close發起斷開連接配接的請求,則通信連接配接可以中斷。

可以通過在主程序中抓取通信端的斷開信号,比如​​

​SIGINT​

​,在信号處理函數中對該通信檔案描述符進行關閉。

close函數介紹

關于close斷開連接配接的缺點:

  • 會一次性将讀寫檔案描述符都關閉
  • 如果多個檔案描述符指向同一個連接配接時(dup函數指派的檔案描述符/或者子程序繼承了父程序的檔案描述符),如果僅僅close其中一個檔案描述符時,隻要其他的fd還是打開狀态,那麼連接配接就不會斷開,直到所有的檔案描述符都被close之後
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>

typedef struct data {
  char name[30];
  unsigned int num;
}Data;

void print_err(char *str, int line, int err_no) {
  printf("%d, %s :%s\n",line,str,strerror(err_no));
  _exit(-1);
}
int cfd = -1;

//線程函數用于循環從cfd描述符中嘗試接收資料
void *receive(void *pth_arg) {
  int ret = 0;
  Data stu_data = {0};
  while(1) {
    //初始化結構體變量
    bzero(&stu_data, sizeof(stu_data));
    ret = recv(cfd, &stu_data, sizeof(stu_data),0); 
    if (-1 == ret) {
      print_err("recv failed",__LINE__,errno);
    }
    //接收之後需要将網絡端序轉換為主機端序
    printf("student number = %d student name = %s \n",ntohl(stu_data.num),stu_data.name);
  }
}

/*信号處理函數,當發生SIGINT信号之後關閉通信的檔案描述符,并傳回成功*/
void sig_fun(int signo) {
        if (signo == SIGINT) {
                close(cfd);
                _exit(0);
        }
}

int main()
{
  int skfd = -1, ret = -1;
  skfd = socket(AF_INET, SOCK_STREAM, 0);
  if ( -1 == skfd) {
    print_err("socket failed",__LINE__,errno);
  }

  struct sockaddr_in addr;
  addr.sin_family = AF_INET; //設定tcp協定族
  addr.sin_port = 6789; //設定端口号
  addr.sin_addr.s_addr = inet_addr("192.168.102.169"); //設定ip位址

  ret = bind(skfd, (struct sockaddr*)&addr, sizeof(addr));
  if ( -1 == ret) {
    print_err("bind failed",__LINE__,errno);
  }
  
  /*将套接字檔案描述符從主動轉為被動檔案描述符,然後用于被動監聽用戶端的連接配接*/
  ret = listen(skfd, 3);
  if ( -1 == ret ) {
    print_err("listen failed", __LINE__, errno);
  }

  /*被動監聽用戶端發起的tcp連接配接請求,三次握手後連接配接建立成功*/
  struct sockaddr_in caddr = {0};
  int csize = 0;
  cfd = accept(skfd, (struct sockaddr*)&caddr, &csize);
  if (-1 == cfd) {
    print_err("accept failed", __LINE__, errno);
  }
  printf("cport = %d, caddr = %s\n", ntohs(caddr.sin_port),inet_ntoa(caddr.sin_addr));
  
  //建立子線程用于接收資料
  pthread_t id;
  pthread_create(&id,NULL,receive,NULL);
  
  //建立信号處理函數
  signal(SIGINT,sig_fun);
  
  //發送資料結構體定義
  Data std_data = {0};
  while (1) {
    printf("stu name:\n");
    scanf("%s",std_data.name);
    
    
    printf("stu num:\n");
    scanf("%d",&std_data.num);
    //對于int型的需要将主機端序轉換為網絡端序,這裡轉成long型。
    std_data.num = htonl(std_data.num);
    
    //将資料std_data強制類型轉換後發送
    ret = send(cfd, (void *)&std_data,sizeof(std_data),0);
    if ( -1 == ret) {
      print_err("accept failed", __LINE__, errno);
    } 
  }
  
  return 0;
}      

shutdown函數介紹

  • 頭檔案 ​

    ​#include <sys/socket.h>​

  • 函數使用:​

    ​int shutdown(int sockfd, int how);​

  • 功能:可以按照要求關閉連接配接,且不管有多個檔案描述符指向同一個連接配接,隻要調用shutdown去操作了其中某個描述符,連接配接就會被立即斷開。
  • 傳回值:成功:傳回0,失敗:傳回-1
  • 參數:

    a. ​​

    ​sockfd​

    ​​ 通信檔案描述符,伺服器端表示accept函數傳回的連結後通信描述符

    b. ​​

    ​how​

    ​​ 如何斷開連接配接

    ​​

    ​SHUT_RD​

    ​​ 隻斷開讀連接配接

    ​​

    ​SHUT_WR​

    ​​隻斷開寫連接配接

    ​​

    ​SHUT_RDWR​

    ​ 讀寫連接配接都斷開