1. 程式人生 > >Linux C基於UDP的網路程式設計

Linux C基於UDP的網路程式設計

套接字(socket)

socket是一種可以進行網路通訊的核心物件,它有一個唯一的識別符號,一般稱它為socket描述符——sockfd,可類比於檔案描述符fd,基於Linux下一切皆檔案的概念,所以sockfd也可以用read/write/close操作。

socket函式:建立socket物件
int socket(int domain, int type, int protocol);
  • domain:通訊地址型別
      AF_UNIX/AF_LOCAL:本地程序間通訊
      AF_INET:使用ipv4地址通訊
      AF_INET6:使用ipv6地址通訊

  • type:socket物件型別
    SOCK_STREAM:

    資料流協議,TCP(面向連線的通訊協議)。特點是安全可靠,資料不會丟失,但速度慢。常用於安全性較高的場景;
    SOCK_DGRAM:資料報協議,UDP(面向無連線的通訊協議)。特點是速度快,資料可能丟失,安全性和可靠性與tcp相比不同。一般用於安全性要求不高但是對速度有要求的場景。

  • protocol:特殊協議
      較少使用,一般直接寫0

  • 返回值:成功返回非負描述符,失敗返回-1
網路通訊地址
struct sockaddr_in
  {
   // 通訊地址型別
   short int sin_family;
   // 埠號
   in_port_t sin_port;
   // ip地址
struct in_addr sin_addr; }
bind函式:把socket物件與通訊地址建立聯絡
 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  • sockfd:socket物件的描述符,即socket函式的返回值
  • struct sockaddr* addr指定了想要繫結的ip和埠號,均用網路位元組序-即大端模式;
  • addrlen是前面struct sockaddr(與sockaddr_in等價)的長度
  • 返回值: 成功返回0,失敗返回-1
connect函式:連線通訊目標
 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
  • 個人計算機系統資料的儲存方式可能是大端,也可能是小端,網路通訊時需要的是大端資料,必須把資料轉換成大端。
uint32_t htonl(uint32_t hostlong);//功能:把32位的主機位元組序轉換成32位網路位元組序
 uint16_t htons(uint16_t hostshort);//功能:把16位的主機位元組序轉換成16位網路位元組序
 uint32_t ntohl(uint32_t netlong);//功能:把32位網路位元組序轉換成32位的主機位元組序
uint16_t ntohs(uint16_t netshort);//功能:把16位網路位元組序轉換成16位的主機位元組序
生成埠號:
  • 埠號就是一個16位的無符整數(0~65536),一般設定為大於1024的值,1~1023為保留埠號。
  • 通常使用htons()函式來獲取埠號。
生成ip地址:
  • 功能:把點分十進位制的字串ip地址轉換成32位的無符號整數
in_addr_t inet_addr(const char *cp);
  • 功能:把32的的網路位元組序的ip地址轉換成點分十進位制的字串ip地址。
char *inet_ntoa(struct in_addr in);
recvfrom函式:接收資料並獲取傳送端的地址
 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
  • buf:資料緩衝區
  • len:緩衝區的大小
  • flag:通常為0
  • src_addr:資料來源端的地址
  • *addrlen:src_addr的長度
sendto函式:傳送資料到指定的目標
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
  • dest_addr:資料要傳送的目標地址

UDP程式設計模型:

程序A:建立socket物件->準備地址->繫結->接收資料和來源的地址->原路返回資料->關閉socket。
程序B:建立socket物件->準備地址->向目標傳送資料->接收資料->關閉socket。

使用udp協議實現雙向傳輸資料(通過ip地址和埠,既可以與自己也可以與別人通訊)。實現程式碼
UDP雙向通訊,先接收端
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>

typedef struct sockaddr* saddrp;

int main(int argc, char const *argv[])
{
     //建立socket
     int sockfd = socket(AF_INET,SOCK_DGRAM,0);
     if (0 > sockfd)
     {
          perror("sockfd");
          return -1;
     }
     //準備地址
     struct sockaddr_in addr = {};
     addr.sin_family = AF_INET;//ipv4
     addr.sin_port = htons(5577);//埠號
     addr.sin_addr.s_addr = inet_addr("192.168.2.177");//我的ip地址
     //繫結
     int ret = bind(sockfd,(saddrp)&addr,sizeof(addr));
     if (0 > ret)
     {
          perror("bind");
          return -1;
     }
     struct sockaddr_in src_addr ={};
     socklen_t addr_len = sizeof(struct sockaddr_in);
     while(1)
     {
          char buf[255] = {};
          //接收資料和來源的ip地址
          recvfrom(sockfd,buf,sizeof(buf),0,(saddrp)&src_addr,&addr_len);
          printf("Recv:%s\n",buf);
          if (0 == strcmp(buf,"q")) break;
          //傳送資料給目標地址
          printf("Please input the return value:");
          gets(buf);
          sendto(sockfd,buf,strlen(buf)+1,0,(saddrp)&src_addr,addr_len);
          if (0 == strcmp(buf,"q")) break;
     }  
     //關閉socket物件
     close(sockfd);
     return 0;
}
UDP實現程式碼 先發送端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
typedef struct sockaddr* saddrp;

int main(int argc, char const *argv[])
{
     int sockfd = socket(AF_INET,SOCK_DGRAM,0);
     if (0 > sockfd)
     {
          perror("socket");
          return -1;
     }
     struct sockaddr_in addr = {};
     addr.sin_family = AF_INET;
     addr.sin_port = htons(5577);
     addr.sin_addr.s_addr = inet_addr("192.168.2.177");

     socklen_t addr_len = sizeof(struct sockaddr_in);
     while(1)
     {
          char buf[255] = {};
          printf("Plz input data:");
          gets(buf);
          sendto(sockfd,buf,strlen(buf)+1,0,(saddrp)&addr,sizeof(addr));
          if(0 == strcmp(buf,"q")) break;

          recvfrom(sockfd,buf,sizeof(buf),0,(saddrp)&addr,&addr_len);
          printf("Recv:%s\n",buf);
          if(0 == strcmp(buf,"q")) break;

     }
     close(sockfd);
     return 0;
}