linux socket 編程(一)

socket詳細介紹:http://blog.csdn.net/hguisu/article/details/7445768/

server_one.c

#include <stdlib.h>

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


int main(int argc, char *argv[])
{
        int                     sockfd;
        int                     new_fd;
        struct sockaddr_in server_addr;
        不過由於系統的兼容性,我們一般不用這個頭文件,而使用另外一個結構(struct sockaddr_in) 來代替.在中有sockaddr_in的定義 
        struct sockaddr_in{
              unsigned short          sin_family;     
              unsigned short int      sin_port;
              struct in_addr          sin_addr;
              unsigned char           sin_zero[8];
        }
        我們主要使用Internet所以
            sin_family一般爲AF_INET,
            sin_addr設置爲INADDR_ANY表示可以和任何的主機通信,
            sin_port是我們要監聽的端口號.sin_zero[8]是用來填充的.


        typedef uint32_t in_addr_t;
        struct in_addr
        {
            in_addr_t s_addr;
        };


        struct sockaddr_in client_addr;
        int                   sin_size;
        int                 portnumber;
        char hello[]="Hello! Are You Fine?\n";


        if(2!=argc)
        {
                fprintf(stderr, "Usage:%s portnumber\n", argv[0]);
                exit(1);
        }


        if((portnumber=atoi(argv[1]))<0)
        {
         /*atoi (表示 ascii to integer)是把字符串轉換成整型數的一個函數. 參數nptr字符串,如果第一個非空格字符存在,是數字或者正負號則開始做類型轉換,之後檢測到非數字(包括結束符 \0) 字符時停止轉換,返回整型數。否則,返回零。*/
                fprintf(stderr, "Usage:%s portnumber\n", argv[0]);
                exit(1);
        }


        /* 服務器端開始建立socket描述符 */
        if((sockfd=socket(AF_INET,  SOCK_STREAM, 0))==-1)  
        {
            /* int socket(int domain, int type,int protocol)
    domain:說明我們網絡程序所在的主機採用的通訊協族(AF_UNIX和AF_INET等).AF_UNIX只能夠用於單一的Unix 系統進程間通信,而AF_INET是針對Internet的,因而可以允許在遠程主機之間通信(當我們 man socket時發現 domain可選項是 PF_*而不是AF_*,因爲glibc是posix的實現所以用PF代替了AF,不過我們都可以使用的).type:我們網絡程序所採用的通訊協議(SOCK_STREAM,SOCK_DGRAM等)  SOCK_STREAM表明我們用的是TCP 協議,這樣會提供按順序的,可靠,雙向,面向連接的比特流. SOCK_DGRAM 表明我們用的是UDP協議,這樣只會提供定長的,不可靠,無連接的通信.protocol:由於我們指定了type,所以這個地方我們一般只要用0來代替就可以了 socket爲網絡通訊做基本的準備.成功時返回文件描述符,失敗時返回-1,看errno可知道出錯的詳細情況.*/


                fprintf(stderr, "Socket error:%s\n", strerror(errno));
        /* stderr(sample standard error)默認輸出到終端窗口,文件描述器代碼爲2.在C中,程序執行時,一直處於開啓狀態。 */
        /* 作用:通過標準錯誤的標號,獲得錯誤的描述字符串 ,將單純的錯誤標號轉爲字符串描述,方便用戶查找錯誤。參數:錯誤標號,通常用errno(標準錯誤號,定義在errno.h中) 返回:指向錯誤信息的指針(即:錯誤的描述字符串) */
                exit(1);
        }


        /* 服務器端填充 sockaddr結構  */ 
        bzero(&server_addr,sizeof(struct sockaddr_in));
        server_addr.sin_family=AF_INET;
        server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
        /*htonl,其實是host to network, l 的意思是返回類型是long,
     將主機數轉換成無符號長整型的網絡字節順序。本函數將一個32位數從主機字節順序轉換成網絡字節順序。所謂網絡字節順序(大尾順序)就是指一個數在內存中存儲的時候“高對低,低對高”(即一個數的高位字節存放於低地址單元,低位字節存放在高地址單元中)。但是計算機的內存存儲數據時有可能是大尾順序或者小尾順序。在網絡上使用統一的網絡字節順序,可以避免兼容性問題。*/
        server_addr.sin_port=htons(portnumber);


        /* 捆綁sockfd描述符  */ 
        if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
        /*用於綁定IP地址和端口號到socket.  int bind(int sockfd, struct sockaddr *my_addr, int addrlen);sockfd是一個socket描述符,my_addr是一個指向包含有本機IP地址及端口號等信息的sockaddr類型的針; addrlen常被設置爲sizeof(struct sockaddr),bind()函數在成功被調用時返回0;遇到錯誤時返回"-1"並將errno置爲相應的錯誤號*/
        {
                fprintf(stderr,"Bind error:%s", strerror(errno));
                exit(1);
        }


        /* 監聽sockfd描述符  */
        if(listen(sockfd,5)==-1)
        /* 設置能處理的最大連接數,listen()並未開始接受連線,只是設置sockect爲listen模式.int listen(int sockfd, int backlog); sockfd是socket系統調用返回的服務器端socket描述符;backlog指定在請求隊列中允許的最大請求數  */
        {
                fprintf(stderr,"Listen error:%s\a", strerror(errno));
                exit(1);
        }


        while(1)
        {
                /* 服務器阻塞,直到客戶程序建立連接  */
                sin_size=sizeof(struct sockaddr_in);
                if((new_fd=accept(sockfd, (struct sockaddr *)(&client_addr), &sin_size))==-1)
                /*用來接受socket連接.int accept(int sockfd, struct sockaddr *addr, int *addrlen); sockfd是被監聽的服務器socket描述符,addr通常是一個指向sockaddr_in變量的指針,該變量用來存放提出連接請求的客戶端地址;addrten通常爲一個指向值爲sizeof(struct sockaddr_in)的整型指針變量。addr,addrlen是用來給客戶端的程序填寫的,服務器端只要傳遞指針就可以了. bind,listen和accept是服務器端用的函數,accept調用時,服務器端的程序會一直阻塞到有一個 客戶程序發出了連接. accept成功時返回最後的服務器端的文件描述符,這個時候服務器端可以向該描述符寫信息了. 錯誤發生時返回一個-1並且設置相應的errno值*/
                {
                        fprintf(stderr,"Accept error:%s\a", strerror(errno));
                        exit(1);
                }


                fprintf(stderr,"Server get connection from %s\n",
                inet_ntoa(client_addr.sin_addr));
                /* 函數聲明:char *inet_ntoa (struct in_addr); 將網絡地址轉換成“.”點隔的字符串格式。若無錯誤發生,inet_ntoa()返回一個字符指針。否則的話,返回NULL。*/
                if(write(new_fd, hello, strlen(hello))==-1)
                {
                        fprintf(stderr,"Write Error:%s\n",strerror(errno));
                        exit(1);
                }
                /* 這個通訊已經結束     */
                close(new_fd);
                /* 循環下一個     */  
        }
        close(sockfd);
        exit(0);

}


client_one.c

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


int main(int argc, char *argv[])
{
        int                     sockfd;
        char              buffer[1024];
        struct sockaddr_in server_addr;
        struct           hostent *host;
        /* hostent是host entry的縮寫,該結構記錄主機的信息,包括主機名、別名、地址類型、地址長度和地址列表。之所以主機的地址是一個列表的形式,原因是當一個主機有多個網絡接口時,自然有多個地址。
        這個數據結構是這樣的: 
        struct hostent { 
           char *h_name; 
           char **h_aliases; 
           int h_addrtype; 
           int h_length; 
           char **h_addr_list; 
       }; 
      #define h_addr h_addr_list[0]  
       這裏是這個數據結構的詳細資料:  
       struct hostent:  
          h_name – 地址的正式名稱。 
          h_aliases – 空字節-地址的預備名稱的指針。 
          h_addrtype –地址類型; 通常是AF_INET。  
          h_length – 地址的比特長度。 
          h_addr_list – 零字節-主機網絡地址指針。網絡字節順序。表示的是主機的ip地址,注意,這個是以網絡字節序存儲的。千萬不要直接用printf帶%s參數來打這個東西,會有問題的哇。所以到真正需要打印出這個IP的話,需要調用inet_ntop()。 
            h_addr - h_addr_list中的第一地址。 
        gethostbyname()成功時返回一個指向結構體 hostent 的指針,或者是個空(NULL)指針。(但是和以前不同,不設置errno,h_errno 設置錯誤信息*/


        int                 portnumber;
        int                     nbytes;


        if(argc!=3)
        {
                fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
                exit(1);
        }


        if((host=gethostbyname(argv[1]))==NULL)
        {
                fprintf(stderr,"Gethostname error\n");
                exit(1);
        }


        if((portnumber=atoi(argv[2]))<0)
        {
                fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
                exit(1);
        }


        /* 客戶程序開始建立 sockfd描述符  */
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
        {
                fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
                exit(1);
        }


        /* 客戶程序填充服務端的資料       */
        bzero(&server_addr,sizeof(server_addr));
        /* extern void bzero(void *s, int n);置字節字符串前n個字節爲零且包括‘\0’。s 要置零的數據的起始地址; n 要置零的數據字節個數。 */


        server_addr.sin_family=AF_INET;
        server_addr.sin_port=htons(portnumber);
        server_addr.sin_addr=*((struct in_addr *)host->h_addr);


        /* 客戶程序發起連接請求         */ 
        if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
        /*  int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)
             sockfd:socket返回的文件描述符.
             serv_addr:儲存了服務器端的連接信息.其中sin_add是服務端的地址
             addrlen:serv_addr的長度
             connect函數是客戶端用來同服務端連接的.成功時返回0,sockfd是同服務端通訊的文件描述符 失敗時返回-1.*/
            
        {
                fprintf(stderr,"Connect Error:%s\a\n", strerror(errno));
                exit(1);
        }


        /* 連接成功了           */
        if((nbytes=read(sockfd, buffer, 1024))==-1)
        {
                fprintf(stderr,"Read Error:%s\n", strerror(errno));
                exit(1);
        }
        buffer[nbytes]='\0';
        printf("I have received:%s\n",buffer);
        /* 結束通訊     */
        close(sockfd);
        exit(0);
}

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