天天看點

C語言網絡程式設計:recv函數詳解

函數描述
  • 頭檔案 ​

    ​#include <sys/socket.h>​

  • ​ssize_t recv(int sockfd, void *buf, size_t len, int flags);​

  • 函數功能:接收對方發送當資料

    可以同樣使用​​

    ​recvfrom​

    ​​函數來接收資料

    ​​

    ​ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);​

    ​​ 當​

    ​recvfrom​

    ​函數的最後兩個參數寫為NULL和0的時候與recv的功能完全一樣
  • 傳回值:成功傳回發送的位元組數;失敗傳回-1,同時errno被設定
  • 函數參數:

    a. ​​

    ​sockfd​

    ​​ 通信檔案描述符

    b. ​​

    ​buf​

    ​​ 應用緩存,用于存放要發送到資料

    可以是任何類型:結構體,int , char,float,字元串

    c. ​​

    ​len​

    ​​ buf的大小

    d. ​​

    ​flags​

    ​​ 一般設定為0,此時send為阻塞式發送

    即發送不成功會一直阻塞,直到被某個信号終端終止,或者直到發送成功為止。

    指定​​

    ​MSG_NOSIGNAL​

    ​​,表示當連接配接被關閉時不會産生​

    ​SIGPIPE​

    ​​信号

    指定​​

    ​MSG_DONTWAIT​

    ​​ 表示非阻塞發送

    指定​​

    ​MSG_OOB​

    ​ 表示帶外資料
代碼執行個體
#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);
  }
}

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);
  
  //發送資料結構體定義
  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;
}