基於數據報 (Datagram) 的編程:(UDP)

軟件控制
程序的運行需要內存、 CPU 和一些系統資源。操作系統關心這類事情,但是一些程序還需要關心另一件事情:就是程序擁有者的允許。
從合法性的角度講,需要有一個許可證來保證程序的合法運行,但是有些許可證卻是帶有限制的。
一種實施許可證技術是編寫程序來執行許可證控制。通常的做法是設計從一個許可證服務器獲得許可的應用程序。該服務器是一個進程,它授權應用程序的運行。
許可證服務器。程序不是從磁盤或密鑰取得許可,而是從服務器進程取得。許可證服務器被一臺計算機上多個用戶共亭,服務器進程能夠控制程序的使用人數、使用時間、使用地點甚至程序的使用方式。
許可證服務系統:
運行許可證程序的過程:
(1)用戶 U 運行被許可的程序巳
(2) 程序 P 向服務器 S 請求運行許可;
(3) 服務器檢查當前運行程序 P 的用戶數;
(4) 如果上限未達到, S 給予許可,程序 P 運行;
(5) 如果達到上限, S 拒絕許可,程序 P 告訴 U 稍後再試。
UDP使用recvfrom 函數阻塞直到數據報到達。當數據報到達時,消息內容、返回地址和其長度將被複制到緩存中。

#include <stdlib.h>
int atoi(const char *nptr);
//函數將由nptr指向的字符串的起始部分轉換爲int。 
#include <unistd.h>
int gethostname(char *name, size_t len);
//gethostname()返回字符數組名稱中的以NULL結尾的主機名,其長度爲len個字節。 如果以null結尾的主機名太大而不適合,則該名稱將被截斷,並且不會返回錯誤
struct hostent *gethostbyname(const char *name);
//函數返回給定主機名稱的hostent類型的結構。這裏的名稱是標準點表示法中的主機名或IPv4地址。如果名稱是IPv4地址,則不執行查找,gethostbyname()僅將name複製到h_name字段中,並將其結構in_addr等同於返回的hostent結構的h_addr_list [0]字段。 如果名稱不以點結尾,並且設置了環境變量HOSTALIASES,則HOSTALIASES指向的別名文件將首先被搜索名稱. 除非名稱以點結尾,否則搜索當前域及其父母。

服務器:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<errno.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<arpa/inet.h>

#define oops(m,x) {perror(m);exit(x);}
#define HOSTLEN 256

    int get_internet_address(char * host,int len,int *portp,struct sockaddr_in *addrp)
{   
  strncpy(host,inet_ntoa(addrp->sin_addr),(size_t)len);
  *portp = ntohs(addrp->sin_port);
  return 0;
}

void say_who_called(struct sockaddr_in *addrp);

int main()
{
  int sock;
  struct sockaddr_in saddr;

  sock = socket(AF_INET,SOCK_DGRAM,0);
  if(sock == -1)
    return -1;

  saddr.sin_port = htons(4444);
  saddr.sin_family = AF_INET;
  saddr.sin_addr.s_addr = inet_addr("192.168.1.127");

  if(bind(sock,(struct sockaddr*)&saddr,sizeof(struct sockaddr))!=0)
    return -1;
  while(1)
  {
    int msglen = 0;
    struct sockaddr_in saddr1;
    char buf[1024] = {0};
    int len = sizeof(struct sockaddr);
    msglen = (int)recvfrom(sock,buf,1024,0,(struct sockaddr*)&saddr1,(socklen_t*)&len);
    if(msglen == -1)
    {
      perror("recvfrom");
      return -1;
    }
    buf[msglen] = '\0';
    printf("dgrecv:got a message:%s\n",buf);
    say_who_called(&saddr);
  }
  return 0;
}

void say_who_called(struct sockaddr_in *addrp)
{
  char host[BUFSIZ];
  int port;

  get_internet_address(host,BUFSIZ,&port,addrp);
  printf("from:%s:%d\n",host,port);
}

客戶端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define oops(m,x) {perror(m);exit(x);}

#define HOSTLEN 256
int make_internet_address(struct sockaddr_in *addrp);

int make_dgram_client_socket()
{
  return socket(PF_INET,SOCK_DGRAM,0);
}

int make_internet_address(struct sockaddr_in *addrp)
{

  memset((void *)addrp,0x00,sizeof(addrp));
  addrp->sin_port = htons(4444);
  addrp->sin_family = AF_INET;
  addrp->sin_addr.s_addr = inet_addr("192.168.1.127");
  return 0;
}

int get_internet_address(char * host,int len,int *portp,struct sockaddr_in *addrp)
{
  strncpy(host,inet_ntoa(addrp->sin_addr),(size_t)len);
  *portp = ntohs(addrp->sin_port);
  return 0;
}
int make_dgram_client_socket();
int make_internet_address(struct sockaddr_in*);

int main()
{
  int sock;
  char msg[BUFSIZ] ={0};
  struct sockaddr_in saddr;
  scanf("%s",msg);

  if((sock = make_dgram_client_socket()) == -1)
    oops("cannot make socket",2);
  make_internet_address(&saddr);
  if(sendto(sock,msg,strlen(msg),0,(struct sockaddr*)&saddr,sizeof(struct sockaddr)) == -1)
    oops("sento failed",3);
  return 0;
}

創建一個數據報 socket 與創建一個流 socket 類似。其不同點在於,這裏設置 socket 的類型爲 SOCK_DGRAM ,而且不要調用 listen 函數。

SOCK_STREAM 提供有序,可靠的雙向連接字節流。 可以支持帶外數據傳輸機制。

SOCK_DGRAM 支持數據報(固定最大長度的無連接,不可靠的消息)。

SOCK_SEQPACKET 爲固定最大長度的數據報提供有序的,可靠的基於雙向連接的數據傳輸路徑; 消費者需要在每次輸入系統調用時讀取整個數據包。

SOCK_RAW 提供原始的網絡協議訪問。

SOCK_RDM 提供不保證排序的可靠數據報層。

SOCK_PACKET 已經過時,不應該在新的程序中使用;

SOCK_NONBLOCK 在新打開的文件描述中設置O_NONBLOCK文件狀態標誌。使用此標誌可以節省對fcntl的額外調用,以實現相同的結果。

SOCK_CLOEXEC 在新文件描述符上設置close-on-exec(FD_CLOEXEC)標誌。 請參閱open(2)中O_CLOEXEC標誌的說明,這可能有用。
這裏寫圖片描述

這裏寫圖片描述

這裏寫圖片描述

總結:
• 數據報是從一個 socket 發送到另一個 socket 的短消息。數據報 socket 是不連接的,
每個消息包含有目的地址。數據報 (UDP)socket 更加簡單、快速,給系統增加的負荷
更小。
• 許可證服務器是用來對被許可程序實施許可證驗證規則的。許可證服務器發佈許
可,以短消息的形式發送給客戶。
• 記錄系統狀態的服務器必須設計成可以處理服務器和客戶端的崩潰事件。
• 有些許可證服務器爲一個網絡上的多個機器提供服務。有兒種設計方法,各有優
缺點。
• socket 可以有兩種類型的地址:網絡或本地。本地的 socket 地址叫做 Unix 域
socket 或名字 socketo 這種 socket 使用文件名作爲地址,只能在一臺機器上交互
數據。

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