顯示具有 C語言 標籤的文章。 顯示所有文章
顯示具有 C語言 標籤的文章。 顯示所有文章

2017年1月19日 星期四

如何用C語言去獲得程式執行的時間?!

如何用C語言去獲得程式執行的時間?!

----------------------------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <time.h>
int main()
{
    const int MOD = 1000000;
    int n,i,j,sum=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        int fact = 1;
        for(j=1;j<=i;j++)
        {
            fact =fact * j % MOD;
        }
        sum = (sum +fact) % MOD;
    }
    printf("%d\n",sum);
    printf("Time used = %.2f\n",(double)clock()/CLOCKS_PER_SEC);

}

----------------------------------------------------------------------------------------------------------------------
我們使用clock()函數來簡單的計時,該函數會返回程式目前為止的時間記錄,在程式結尾呼叫此函數,便可以獲得整個程式的執行時間,CLOCKS_PER_SEC是表示說得到的值我們以秒為單位。



2015年10月11日 星期日

Concurrent TCP server網路程式設計(2):

  在Linux中,網路程式設計主要全靠socket來接收及傳送資料,本次我們要探討多個client端連接至一台伺服器的情形。
  若要讓TCP server同時處理多個client的連線,最簡單的方法便是使用concurrent TCP server架構(如下圖)。






















Concurrent TCP server 架構:

  所謂的[Concurrent TCP server]架構,是由父程序產生多個子程序,每個子程序分別與建立連線的client端進行資料傳輸,在傳輸的過程中,父程序只負責連線的接收,每次連線成功便產生對應的子程序。

如何產生子程序?

  我們可以讓server呼叫 fork() 函數來產生子程序,  fork() 函數會將新建立的通訊端複製到子程序中,在子程序中可以與連上的client端進行通訊。


  一個concurent TCP server的基礎程式架構如下。

--------------------------------------------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
  int len_inet;
  struct sockaddr_in adr_srvr,adr_clnt;
  pid_t PID;

  /*建立listening socket*/

  sockfd = socket(AF_INET,SOCK_STREAM,0);

  /*連結socket*/

  bind(sockfd,(struct sockaddr*)&adr_srvr,sizeof(adr_srvr));

  /*傾聽client*/

  listen(sockfd,10);

  while(1)
  {
     len_inet = sizeof(adr_clnt);

     /*接受client端連線請求,建立connected socket*/

     connfd = accept(sockfd, (struct sockaddr *)&adr_clnt, &len_inet);

    /*建立子程序*/

    PID = fork();
    if(PID>0)
     {
        /*父程序關閉connected socket*/
        
        close(connfd);

        /*返回while開頭*/
        
        continue;
     }
    printf("子程序處理....\n");

    /*子程序關閉listening socket*/

    close(sockfd);

    /*與連線client端進行資料傳輸*/
  
    exit(0);
  }
 return 0;
}
-------------------------------------------------------------------------------------------------------------------

由上述的基礎程式架構,可以發現建構concurrent TCP server的一些原則:

1. 在呼叫fork()函數前,我們必須呼叫socket()、bind()、listen()與accept(),來建立listening socket(sockfd),以接收來自client端的連線請求
2. 當server接受client端的連線請求時,會傳回connected socket(connfd)
3. 在呼叫fork()後,若為父程序,則關閉connected socket,回到while迴圈的起始點,等待另一個client端的請求。
4. 在呼叫 fork() 後,若為子程序,則關閉listening socket,並與連線的client端進行資料的輸出輸入,完畢後結束程序,並回傳 0。


注意Zombie程序:

  當在設計這種concurrent TCP server架構時,由於我們會使用fork()函數,子程序在呼叫exit()結束後,會進入zombie程序,若父程序不處理這些zombie程序,便會常駐在記憶體中,最後會拖垮整個系統的效率!
  所以我們可以在建立子程序時,透過接受SIGCHILD訊息,撰寫相關的訊息處理函數,一個SIGCHILD訊息處理函數程式碼如下。
-------------------------------------------------------------------------------------------------------------------
static void sigchld_handler() {
  pid_t PID;
  int status;

  while (PID = waitpid(-1, &status, WNOHANG) > 0)
 printf("子程序 %d 結束.\n", PID);

  /* Re-install handler */
  signal(SIGCHLD, sigchld_handler);
}
------------------------------------------------------------------------------

上述程式碼呼叫了 waitpid()函數來清除zombie程序,waitpid()函數的第一個引數為[-1],表示等待任何子程序,第二個引數用來儲存子程序結束的狀態,第三個引數設為[WNOHANG],表示當沒有任何zombie程序時,馬上返回,不予等待。

由上述的介紹後,我們還練習撰寫一個網路多工的程式。

動作要求:

1. server程式在執行後,可以接受多個client的連線要求。
2. server程式會在client端連線後,以每隔一秒的時間,將目前的系統時間傳送至client端
3. client端會接收server端傳來的訊息,並將訊息顯示出來。

程式:server端

/* multi_server2.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

static void sigchld_handler() {
  pid_t PID;
  int status;

  while (PID = waitpid(-1, &status, WNOHANG) > 0)
 printf("子程序 %d 結束.\n", PID);

  /* Re-install handler */
  signal(SIGCHLD, sigchld_handler);
}

int main(int argc, char **argv) {
  int  z, len_inet;
  struct sockaddr_in adr_srvr, adr_clnt;
  int  sockfd, connfd;
  pid_t  PID;
  time_t clock;
  struct tm *tm;
  int  len_time, i;
  char  buf[256];
  
  signal(SIGCHLD, sigchld_handler);
  
  memset(&adr_srvr, 0, sizeof(adr_srvr));
  
  if (argc ==2)
 adr_srvr.sin_addr.s_addr = inet_addr(argv[1]);
  else
 adr_srvr.sin_addr.s_addr = inet_addr("192.168.1.20");
  

  adr_srvr.sin_family = AF_INET;
  adr_srvr.sin_port = htons(9090);
 
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd == -1) {
 perror("socket error");
 exit(1);
  }

  z = bind(sockfd, (struct sockaddr *)&adr_srvr, sizeof(adr_srvr));
  if (z == -1) {
 perror("bind error");
 exit(1);
  }

  z = listen(sockfd, 10);
  if (z == -1) {
 perror("listen error");
 exit(1);
  }

  while(1) {
 len_inet = sizeof(adr_clnt);
 connfd = accept(sockfd, (struct sockaddr *)&adr_clnt, &len_inet);
 if (connfd == -1) {
  perror("connect error");
  exit(1);
 }
 
 PID = fork();

 if (PID > 0) {
  close(connfd);
  continue;
 }

 printf("子程序處理...\n");
 close(sockfd);
 for (i=0; i<10; i++) {
  sleep(1);
  time(&clock);
  tm = gmtime(&clock);
  len_time = strftime(buf, 256, "%A %T %D\n", tm);
 
  if (write(connfd, buf, len_time) == -1) {
   perror("write error");
   exit(1);
  }
 }
 sleep(1);
 sprintf(buf, "stop\n");
 write(connfd, buf, sizeof(buf));
 exit(0);
  }
  return 0;
}

程式:client端

/* multi_client.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

int main(int argc, char **argv) {
  int z, len_inet;
  struct sockaddr_in adr_srvr;
  int sockfd;
  int i;
  char buf[256];
  
  memset(&adr_srvr, 0, sizeof(adr_srvr));
  
  if (argc ==2)
 adr_srvr.sin_addr.s_addr = inet_addr(argv[1]);
  else
 adr_srvr.sin_addr.s_addr = inet_addr("192.168.1.20");
  

  adr_srvr.sin_family = AF_INET;
  adr_srvr.sin_port = htons(9090);
 
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd == -1) {
 perror("socket error");
 exit(1);
  }

  z = connect(sockfd, (struct sockaddr *)&adr_srvr, sizeof(adr_srvr));
  if (z == -1) {
 perror("connect error");
 exit(1);
  }
  
  while (1) {
   z = read(sockfd, buf, 256);
   if (z == -1) {
  perror("read error");
  exit(1);
   }
   buf[z]=0;

 if (strncmp(buf, "stop", 4) == 0)
  break;
 
 printf("日期時間為: %s\n", buf); 
  }
  close(sockfd);
  return 0;
}

2015年10月5日 星期一

Socket網路程式設計(1):

    Linux OS使用Socket的觀念來設計網路程式,所以在介紹UDP,TCP網路程式設計前,先了解一下Socket的基本觀念。

    "Socket"有時又稱為[接口],是一種可做雙向資料傳輸的通道,可以將Socket想像成一種裝置,Linux程序可經由此裝置與本地端或是遠端的程序溝通。

    本次要介紹的UDP及TCP的網路程式主要是用到Internet-domain socket,如果我們能了解Internet-domain socket結構的定義相關函數的意義,就能寫出UDP及TCP的網路程式。

結構與相關函數:
        
        在設計網路程式之前,先說明Internet-domain socket的定址結構,此結構可以用來儲存IP位址、通訊口等訊息。
     

[sockaddr_in]定址結構:

    struct sockaddr_in{
             sa_family_t  sin_family;  /*AF_INET*/
             unsigned short int  sin_port;  /*port number*/
             struct in_addr  sin_addr;  /*Internet address*/
};

  sin_family:是用來說明socket所使用的定址模式,在此必須設為[AF_INET],表示是Internet-                          domain socket。
  sin_port:用來表示TCP/IP的port number,設定sin_port必須使用htons函數做位元排序的動作,                 會在之後說明。
  sin_addr:用來表示IP位址,結構如下。
                  struct in_addr{
                            unsigned long int s_addr;/*儲存IP位址*/
                   };

[inet_addr()函數]:

  inet_addr()函數可用來將[xxx.xxx.xxx.xxx]格式的IP位址轉換成ˇ32-bit unsigned integer。
  [格式]
             #include <sys/socket.h>
             #include <netinet/in.h>
             #include <arpa/inet.h> 
             unsigned long inet_addr(const char *string);
其中[string]為一個IP字串,他的格式為[XXX.XXX.XXX.XXX] 


位元排序函數:   

 在設定IPv4 Socket定址結構時,還有一點需要注意的是port number 的設置。
 假如我們要設定IP位址[192.168.1.10],port number為[8000],我們會做如下設定
  
  struct sockaddr_in adr_srvr;
  adr_srvr.sin_addr.s_addr = inet_addr("192.168.10");
  adr_srvr.sin_port = htons(8000);


  Socket():

  要完成TCP及UDP網路程式設計,需要呼叫許多的SOCKET函數,來幫助我們撰寫網路程式,不論是使用TCP及UDP作為傳輸協定,要透過SOCKET做資料傳輸,首要的工作就是建立SOCKET,要建立SOCKET就可使用socket()函數。

[Socket格式]

                       #include <sys/types.h>
                       #include <sys/socket.h>
                       int socket(int domain, int type, int protocol);

domain:網域,只要設定為[AF_INET]即可,表示使用Internet協定。

type:連結的型態,若設定值為[SOCK_STREAM]表示為TCP傳輸層協定,若設定值為                         [SOCK_DGRAM]表示為UDP傳輸層協定。

protocol:通訊協定,一般設為[0],表示自動選擇。


  Bind():

在通訊協定中我們可以使用Bind()函數將IPv4 socket定址結構,連結到我們所建立的socket,如此一來每當有封包到達網路介面時,Linux核心就會將這個封包導向所連結的socket。


[Bind格式]

                   #include<sys/socket.h>
                   int bind (int sockfd, const struct sockaddr *my_addr, size_t adr_len);

sockfd: socket函數執行後所傳回的socket描述子

my_addr:指向struct sockaddr_in結構的指標,用來存放欲連結的IPv4定址結構

adr_len: struct sockaddr_in結構的長度,可將其指定為sizeof(struct sockaddr_in)

Listen():

建立好socket並做好bind後,在server端,我們可以使用listen()函數來通知Linux核心,將socket設為[listening socket],等待client端的連線要求。

[Listen格式]

                    #include <sys/socket.h>
                    int listen(int sockfd, int backlog);

sockfd: socket函數執行後所傳回的socket描述子

backlog:指定最大連線數量,通常設為5

Accept():

當server端建立好listening socket並接收到client端的連線請求時,就會將連線請求放入連線佇列中,接著server端必須呼叫accept()函數,來處理並接受佇列中的連線請求。

[Accept格式]

                    #include <sys/socket.h>
                    int accept(int sockfd, struct sockaddr *addr, socklen_t addrlen);

sockfd: socket函數執行後所傳回的socket描述子

addr:指向struct sockaddr_in結構的指標,用來存放client端的IP address

addrlen:存放client IP address變數的長度,初值應為sizeof(struct sockaddr_in),accept()執行成                 功後會存回實際的client IP address 長度


Connect():

在TCP中,當client端建立好了socket後可以呼叫connect()函數向server端要求建立連線,建立完畢後才能互傳資料。

[Connect格式]

                    #include <sys/socket.h>
                    int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

sockfd: socket函數執行後所傳回的socket描述子

serv_addr:指向struct sockaddr_in結構的指標,用來存放server端的IP address

addrlen: struct sockaddr_in結構的長度,可指定為sizeof(struct sockaddr_in)

Close():

我們可以呼叫close()函數來中止連線。

[Close格式]

                    #include <sys/socket.h>
                    int close(int sockfd);

sockfd: socket函數執行後所傳回的socket描述子

TCP程式設計流程:

在設計網路程式我們會分為客戶端與伺服端,綜合以上的說明,我們可以將Linux TCP程式設計流程,以下圖表示。





TCP輸出入函數:

當我們在server端建立了socket做好bind,產生listening socket,接受client端連線要求後,server端便可以與client端彼此傳遞資料,要傳遞資料,需要使用輸出入函數,TCP socket的輸出入函數有許多種,最基礎的便是read()、write()函數。

Read():

read()函數會從已開啟的socket讀取資料。

[Read格式]

                   #include <sys/socket.h>
                   int read(int sockfd, char *buf, int len);


sockfd: socket函數執行後所傳回的socket描述子

buf:指向字元暫存器的指標,用來儲存讀取到的資料

len:欲讀取的字元長度

Write():

write()函數可以將資料寫入已開啟的socket。

[Read格式]

                   #include <sys/socket.h>
                   int write(int sockfd, char *buf, int len);

sockfd: socket函數執行後所傳回的socket描述子

buf:指向字元暫存器的指標,用來儲存欲寫入的資料

len:欲寫入的字元長度

Recv():

recv()函數可以經由socket來接收資料。

[Recv格式]

                   #include <sys/types.h>
                   #include <sys/socket.h>
                   int recv(int s, void *buf, int len, unsigned int flags);
[note]:recv()用來接收遠端socket傳來的資料,並把資料儲存在buf指向的記憶體中,len為可接              收的最大長度,flags一般設為0

Send():

send()函數可以經由socket來傳送資料。

[Send格式]

                   #include <sys/types.h>
                   #include <sys/socket.h>
                   int send(int s, const void *msg, int len, unsigned int flags);
[note]:send()函數可以將msg指向的資料,經由socket傳送給遠端的主機,len則為傳送資料最大            長度,flags一般設為0。

--------------------------------------------------------------------------------------------------------------------------
由上述介紹後,我們來實作TCP通訊協定的server程式與client程式

動作要求:

1.server端會建立socket,連結socket,等待client端連線
2.client端會建立socket,與server連線,若成功,將str字串傳給server,str字串內容為[品號 品      名 售價]
3.server端若接受client端的連線,會讀取字串並解析內容,算出售價的含稅價,再將處理後的    字串回傳給client
4.client端收到server端的訊息,會將訊息顯示在螢幕上

程式:server端

/* server.c */

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

int port = 8000;

int main(void)
{
  struct sockaddr_in sin;
  struct sockaddr_in pin;
  int mysock;
  int tempsock;
  int addrsize;
  char str[100],str1[20],str2[20],str3[20];
  char buf[100];
  int i, len1, len2;
  float c;

  mysock = socket(AF_INET, SOCK_STREAM, 0);
  if (mysock == -1) {
  perror("call to socket");
  exit(1);
  }

  bzero(&sin, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr = htonl(INADDR_ANY);
  sin.sin_port = htons(port);

  if (bind(mysock, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
  perror("call to bind");
  exit(1);
  }

  if (listen(mysock, 20) == -1) {
  perror("call to listen");
  exit(1);
  }

  printf("Accepting connections ...\n");

  while(1) {
  tempsock = accept(mysock, (struct sockaddr *)&pin, &addrsize);
  if (tempsock == -1){
    perror("call to accept");
    exit(1);
  }

  len1=recv(tempsock, str, 100, 0);
  printf("\n收到字元數: %d\n",len1);
str[len1]=0;
printf("received from client: %s\n", str);

if (len1 > 0) {
strcpy(str1,strtok(str," "));
printf("第 1 個字串為: %s\n",str1);
strcpy(str2,strtok(NULL," "));
printf("第 2 個字串為: %s\n",str2);
strcpy(str3,strtok(NULL," "));
printf("第 3 個字串為: %s\n",str3);
c=atof(str3)*1.05;
sprintf(buf,"品號為 %s\n品名為 %s\n含稅價為: %.2f\n",str1, str2, c);
}
  len2 = strlen(buf);

  if (send(tempsock, buf, len2, 0) == -1) {
    perror("call to send");
    exit(1);
  }
  close(tempsock);
  }
  return 0;
}

程式:client端

/* client.c */

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

int port = 8000;

int main(int argc, char *argv[])
{
  struct sockaddr_in pin;
  int mysock;
  char buf[8192];
  char *str="A001 電視機 20000.00 ";

  if (argc < 2) {
printf("使用方法: client 字串\n");
printf("使用預設字串\n");
  } else {
str=argv[1];
  }
  
  bzero(&pin, sizeof(pin));
  pin.sin_family = AF_INET;
  pin.sin_addr.s_addr = inet_addr("192.168.1.20");
  pin.sin_port = htons(port);

  mysock = socket(AF_INET, SOCK_STREAM, 0);
  if (mysock == -1) {
  perror("call to socket");
  exit(1);
  }

  if (connect(mysock, (void *)&pin, sizeof(pin)) == -1) {
  perror("call to connect");
  exit(1);
  }

  printf("Sending message %s to server ...\n", str);

  if (send(mysock, str, strlen(str), 0) == -1) {
  perror("Error in send\n");
  exit(1);
  }


  if (recv(mysock, buf, 8192, 0) == -1) {
  perror("Error in receiving\n");
  exit(1);
  }

  printf("\nResponse from server: \n\n%s\n", buf);

  close(mysock);
  return 0;
}
--------------------------------------------------------------------------------------------------------------------------

 sin.sin_addr.s_addr = htonl(INADDR_ANY):

       讓Server端的socket可以接收同一個網域內所有的IP位址,INADDR_ANY可視為[萬用位            址],同樣地,若要讓socket接收同區網的所有port number,指令如下:
        sin.sin_port = htons(0);

指令列引數:

int main(int argc, char *argv[]){....}
此為C語言的一項功能,此功能可以讓程式在執行時,接受由使用者輸入一些字串,作為主程式的參數,其中第一個引數[argc]會儲存使用者輸入的字串個數,而第二個引數[argvp[]]為字串指標陣列,指向使用者所輸入的字串內容。
例如,當我們執行client程式可以輸入以下字串:
./client "A002 音響 15000.00"
此時,argc的值為2,argv[0]的內容為client,argv[1]的內容為"A002 音響 15000.00"。