Linux網絡編程簡單示例

轉自:http://blog.chinaunix.net/uid-25695950-id-4485000.html
示例程序是國嵌的

linux 網絡編程是通過socket(套接字)接口實現,Socket是一種文件描述符,socket起源於UNIX,在Unix一切皆文件哲學的思想下,socket是一種"打開—讀/寫—關閉"模式的實現,服務器和客戶端各自維護一個"文件",在建立連接打開後,可以向自己文件寫入內容供對方讀取或者讀取對方內容,通訊結束時關閉文件。

socket 類型

    常見的socket有3種類型如下。 
    (1)流式socket(SOCK_STREAM ) 
    流式套接字提供可靠的、面向連接的通信流;它使用TCP 協議,從而保證了數據傳輸的正確性和順序性。 
    (2)數據報socket(SOCK_DGRAM ) 
    數據報套接字定義了一種無連接的服 ,數據通過相互獨立的報文進行傳輸,是無序的,並且不保證是可靠、無差錯的。它使用數據報協議UDP。 
    (3)原始socket(SOCK_RAW)
    原始套接字允許對底層協議如IP或ICMP進行直接訪問,功能強大但使用較爲不便,主要用於一些協議的開發。


進行Socket編程,常用的函數有:

1.socket:創建一個socket

  int socket(int family, int type, int protocol);

//family指定協議族;type參數指定socket的類型:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;protocol通常賦值"0", socket()調用返回一個整型socket描述符


2.bind:用於綁定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置爲相應的錯誤號


3.connect:該函數用於綁定之後的client端,與服務器建立連接

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

//sockfd是目的服務器的sockect描述符;serv_addr是服務器端的IP地址和端口號的地址,addrlen常被設置爲sizeof(struct sockaddr)。遇到錯誤時返回-1,並且errno中包含相應的錯誤碼


4.listen:設置能處理的最大連接數,listen()並未開始接受連線,只是設置sockect爲listen模式

 int listen(int sockfd, int backlog); 

// sockfd是socket系統調用返回的服務器端socket描述符;backlog指定在請求隊列中允許的最大請求數    


5.accept:用來接受socket連接

int accept(int sockfd, struct sockaddr *addr, int *addrlen); 

//sockfd是被監聽的服務器socket描述符,addr通常是一個指向sockaddr_in變量的指針,該變量用來存放提出連接請求的客戶端地址;addrten通常爲一個指向值爲sizeof(struct sockaddr_in)的整型指針變量。錯誤發生時返回一個-1並且設置相應的errno值


6.send:發送數據         

int send(int sockfd, const void *msg, int len, int flags); 

//sockfd是你想用來傳輸數據的socket描述符,msg是一個指向要發送數據的指針。 len是以字節爲單位的數據的長度。flags一般情況下置爲0


7.recv:接受數據        

int recv(int sockfd,void *buf,int len,unsigned int flags); 

//sockfd是接受數據的socket描述符;buf 是存放接收數據的緩衝區;len是緩衝的長度。flags也被置爲0。recv()返回實際上接收的字節數,或當出現錯誤時,返回-1並置相應的errno值。


8.sendto:發送數據,用於面向非連接的socket(SOCK_DGRAM/SOCK_RAW)

int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to,int tolen); 

//該函數比send()函數多了兩個參數,to表示目地機的IP地址和端口號信息,而tolen常常被賦值爲sizeof (struct sockaddr)。sendto 函數也返回實際發送的數據字節長度或在出現發送錯誤時返回-1。


9.recvform: 接受數據,用於面向非連接的socket(SOCK_DGRAM/SOCK_RAW)

int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen); 

//from是一個struct sockaddr類型的變量,該變量保存源機的IP地址及端口號。fromlen常置爲sizeof(struct sockaddr)。當recvfrom()返回時,fromlen包含實際存入from中的數據字節數。Recvfrom()函數返回接收到的字節數或當出現錯誤時返回-1,並置相應的errno   


幾個字節順序轉換函數: 

    htons()   --"Host to Network Short" ; htonl()--"Host to Network Long" 
    ntohs()   --"Network to Host Short" ; ntohl()--"Network to Host Long" 
    在這裏, h表示"host" ,n表示"network",s 表示"short",l表示 "long"。

地址轉換函數:

    in_addr_t inet_addr(const char * strptr);
    將字符串IP地址轉換爲IPv4地址結構in_addr值

    char * inet_ntoa(struct in_addr * addrptr);
    將IPv4地址結構in_addr值轉換爲字符串IP

    域名和IP地址的轉換: 
    struct hostent *gethostbyname(const char *name); 
    函數返回一種名爲hostent的結構類型,它的定義如下: 
    struct hostent 
    { 
         char *h_name;        /* 主機的官方域名 */ 
         char **h_aliases;    /* 一個以NULL結尾的主機別名數組 */ 
         int    h_addrtype;   /* 返回的地址類型,在Internet環境下爲AF-INET */ 
         int h_length;        /*地址的字節長度 */ 
         char **h_addr_list;  /* 一個以0結尾的數組,包含該主機的所有地址*/ 
  }; 
    #define h_addr h_addr_list[0] /*在h-addr-list中的第一個地址*/


下面給出兩個示例,一個是面向TCP數據流的socket通信,一個是面向UDP數據報的socket通信

1.面向TCP數據流的socket通信的演示程序由基於TCP的服務器和基於TCP的客戶端程序組成。

    TCP的服務器程序結構:

    1.創建一個socket,用函數socket()

    2.綁定IP地址、端口信息到socket上,用函數bind()

    3.設置允許的最大連接數,用函數listen()

    4.接受客戶端的連接,用函數accept()

    5.收發數據,用send()、recv()或者read()、write()

    6.關閉網絡連接

    TCP的客戶端程序結構:

    1.創建一個socket,用函數socket()

    2.設置要連接的服務器的IP地址和端口屬性

    3.連接服務器,用函數connet()

    4.收發數據,用send()、recv()或者read()、write()

    5.關閉網絡連接

TCP服務器和TCP客戶端的通信圖如下:

下面貼出TCP服務器的代碼tcp_server.c:

tcp_server.c

  1. #include <stdlib.h> 
  2. #include <stdio.h> 
  3. #include <errno.h> 
  4. #include <string.h> 
  5. #include <netdb.h> 
  6. #include <sys/types.h> 
  7. #include <netinet/in.h> 
  8. #include <sys/socket.h> 
  9. #include <arpa/inet.h> //inet_ntoa()函數的頭文件

  10. #define portnumber 3333 //定義端口號:(0-1024爲保留端口號,最好不要用)

  11. int main(int argc, char *argv[]) 
  12. { 
  13.     int sockfd,new_fd; 
  14.     struct sockaddr_in server_addr; //描述服務器地址
  15.     struct sockaddr_in client_addr; //描述客戶端地址
  16.     int sin_size; 
  17.     char hello[]="Hello! Are You Fine?\n"; 
  18.     

  19.     /* 服務器端開始建立sockfd描述符 */ 
  20.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4;SOCK_STREAM:TCP
  21.     { 
  22.         fprintf(stderr,"Socket error:%s\n\a",strerror(errno)); 
  23.         exit(1); 
  24.     } 

  25.     /* 服務器端填充 sockaddr結構 */ 
  26.     bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0
  27.     server_addr.sin_family=AF_INET; // Internet
  28.     server_addr.sin_addr.s_addr=htonl(INADDR_ANY); // (將本機器上的long數據轉化爲網絡上的long數據)和任何主機通信 //INADDR_ANY 表示可以接收任意IP地址的數據,即綁定到所有的IP
  29.     //server_addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用於綁定到一個固定IP,inet_addr用於把數字加格式的ip轉化爲整形ip
  30.     server_addr.sin_port=htons(portnumber); // (將本機器上的short數據轉化爲網絡上的short數據)端口號
  31.     
  32.     /* 捆綁sockfd描述符到IP地址 */ 
  33.     if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) 
  34.     { 
  35.         fprintf(stderr,"Bind error:%s\n\a",strerror(errno)); 
  36.         exit(1); 
  37.     } 

  38.     /* 設置允許連接的最大客戶端數 */ 
  39.     if(listen(sockfd,5)==-1) 
  40.     { 
  41.         fprintf(stderr,"Listen error:%s\n\a",strerror(errno)); 
  42.         exit(1); 
  43.     } 

  44.     while(1) 
  45.     { 
  46.         /* 服務器阻塞,直到客戶程序建立連接 */ 
  47.         sin_size=sizeof(struct sockaddr_in); 
  48.         if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1) 
  49.         { 
  50.             fprintf(stderr,"Accept error:%s\n\a",strerror(errno)); 
  51.             exit(1); 
  52.         } 
  53.         fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); // 將網絡地址轉換成.字符串,並打印到輸出終端
  54.         
  55.         //向客戶端程序寫入hello數組裏的字符
  56.         if(write(new_fd,hello,strlen(hello))==-1) 
  57.         { 
  58.             fprintf(stderr,"Write Error:%s\n",strerror(errno)); 
  59.             exit(1); 
  60.         } 
  61.         /* 這個通訊已經結束 */ 
  62.         close(new_fd); 
  63.         /* 循環下一個 */ 
  64.     } 

  65.     /* 結束通訊 */ 
  66.     close(sockfd); 
  67.     exit(0); 
  68. }
TCP客戶端的代碼tcp_client.c

tcp_client.c

  1. #include <stdlib.h> 
  2. #include <stdio.h> 
  3. #include <errno.h> 
  4. #include <string.h> 
  5. #include <netdb.h> 
  6. #include <sys/types.h> 
  7. #include <netinet/in.h> 
  8. #include <sys/socket.h> 
  9. #include <arpa/inet.h> //inet_ntoa()函數的頭文件


  10. #define portnumber 3333 //定義端口號:(0-1024爲保留端口號,最好不要用)

  11. int main(int argc, char *argv[]) 
  12. { 
  13.     int sockfd; 
  14.     char buffer[1024]; 
  15.     struct sockaddr_in server_addr; //描述服務器的地址
  16.     struct hostent *host; 
  17.     int nbytes; 

  18.     /* 使用hostname查詢host 名字 */
  19.     if(argc!=2) 
  20.     { 
  21.         fprintf(stderr,"Usage:%s hostname \a\n",argv[0]); 
  22.         exit(1); 
  23.     } 

  24.     if((host=gethostbyname(argv[1]))==NULL) 
  25.     { 
  26.         fprintf(stderr,"Gethostname error\n"); 
  27.         exit(1); 
  28.     } 

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

  35.     /* 客戶程序填充服務端的資料 */ 
  36.     bzero(&server_addr,sizeof(server_addr)); // 初始化,置0
  37.     server_addr.sin_family=AF_INET; // IPV4
  38.     server_addr.sin_port=htons(portnumber); // (將本機器上的short數據轉化爲網絡上的short數據)端口號
  39.     server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址
  40.     
  41.     /* 客戶程序發起連接請求 */ 
  42.     if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) 
  43.     { 
  44.         fprintf(stderr,"Connect Error:%s\a\n",strerror(errno)); 
  45.         exit(1); 
  46.     } 

  47.     /* 連接成功了 */ 
  48.     if((nbytes=read(sockfd,buffer,1024))==-1) 
  49.     { 
  50.         fprintf(stderr,"Read Error:%s\n",strerror(errno)); 
  51.         exit(1); 
  52.     } 
  53.     buffer[nbytes]='\0'; 
  54.     printf("I have received:%s\n",buffer); 

  55.     /* 結束通訊 */ 
  56.     close(sockfd); 
  57.     exit(0); 
  58. }

編譯tcp_server.c和tcp_client.c

gcc tcp_server.c -o tcp_server

gcc tcp_client.c -o tcp_client

運行tcp服務器段程序和客戶端程序,顯示過程截圖如下:




2.面向UDP數據報的socket通信的演示程序由基於UCP的服務器和基於UDP的客戶端程序組成。

    UDP的服務器程序結構:

    1.創建一個socket,用函數socket()

    2.綁定IP地址、端口信息到socket上,用函數bind()

    3.循環接受數據,用函數recvform()

    4.關閉網絡連接

   UDP的客戶端程序結構:

    1.創建一個socket,用函數socket()

    2.設置要連接的服務器的IP地址和端口屬性

    3.發送數據,用函數sento()

    4.關閉網絡連接

UDP服務器和UDP客戶端的通信圖如下:


下面貼出UDP服務器的代碼udp_server.c:

udp_serer.c

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <netdb.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <sys/types.h>
  10. #include <arpa/inet.h>

  11. #define SERVER_PORT 8888 //定義端口號:(0-1024爲保留端口號,最好不要用)
  12. #define MAX_MSG_SIZE 1024 

  13. void udps_respon(int sockfd) 
  14. { 
  15.     struct sockaddr_in addr; 
  16.     int addrlen,n; 
  17.     char msg[MAX_MSG_SIZE]; 

  18.     while(1) 
  19.     {    /* 從網絡上讀,並寫到網絡上 */ 
  20.         bzero(msg,sizeof(msg)); // 初始化,清零
  21.         addrlen = sizeof(struct sockaddr); 
  22.         n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,(struct sockaddr*)&addr,&addrlen); // 從客戶端接收消息
  23.         msg[n]=0; 
  24.         /* 顯示服務端已經收到了信息 */ 
  25.         fprintf(stdout,"Server have received %s",msg); // 顯示消息
  26.     } 
  27. } 

  28. int main(void) 
  29. { 
  30.     int sockfd; //socket描述符
  31.     struct sockaddr_in addr; //定義服務器起地址

  32.     /* 服務器端開始建立socket描述符 */ 
  33.     sockfd=socket(AF_INET,SOCK_DGRAM,0); 
  34.     if(sockfd<0) 
  35.     { 
  36.         fprintf(stderr,"Socket Error:%s\n",strerror(errno)); 
  37.         exit(1); 
  38.     } 

  39.     /* 服務器端填充 sockaddr結構 */ 
  40.     bzero(&addr,sizeof(struct sockaddr_in)); // 初始化,置0
  41.     addr.sin_family=AF_INET; // Internet
  42.     addr.sin_addr.s_addr=htonl(INADDR_ANY); // (將本機器上的long數據轉化爲網絡上的long數據)和任何主機通信 //INADDR_ANY 表示可以接收任意IP地址的數據,即綁定到所有的IP
  43.     //addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用於綁定到一個固定IP,inet_addr用於把數字加格式的ip轉化爲整形ip
  44.     addr.sin_port=htons(SERVER_PORT); // (將本機器上的short數據轉化爲網絡上的short數據)端口號

  45.     /* 捆綁sockfd描述符 */ 
  46.     if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0) 
  47.     { 
  48.         fprintf(stderr,"Bind Error:%s\n",strerror(errno)); 
  49.         exit(1); 
  50.     } 

  51.     udps_respon(sockfd); // 進行讀寫操作
  52.     close(sockfd); 
  53. }
UDP客戶端的代碼udp_client.c:

udp_client.c

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <netdb.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <sys/types.h>
  10. #include <arpa/inet.h>

  11. #define SERVER_PORT 8888 //定義端口號:(0-1024爲保留端口號,最好不要用)
  12. #define MAX_BUF_SIZE 1024 

  13. void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len) 
  14. { 
  15.     char buffer[MAX_BUF_SIZE]; 
  16.     int n; 
  17.     while(1) 
  18.     {     /* 從鍵盤讀入,寫到服務端 */ 
  19.         printf("Please input char:\n");
  20.         fgets(buffer,MAX_BUF_SIZE,stdin); 
  21.         sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)addr,len); 
  22.         bzero(buffer,MAX_BUF_SIZE); 
  23.     } 
  24. } 

  25. int main(int argc,char **argv) 
  26. { 
  27.     int sockfd; //socket描述符
  28.     struct sockaddr_in addr; //定義服務器起地址

  29.     if(argc!=2) 
  30.     { 
  31.         fprintf(stderr,"Usage:%s server_ip\n",argv[0]); 
  32.         exit(1); 
  33.     }

  34.     /* 建立 sockfd描述符 */ 
  35.     sockfd=socket(AF_INET,SOCK_DGRAM,0); 
  36.     if(sockfd<0) 
  37.     { 
  38.         fprintf(stderr,"Socket Error:%s\n",strerror(errno)); 
  39.         exit(1); 
  40.     } 

  41.     /* 填充服務端的資料 */ 
  42.     bzero(&addr,sizeof(struct sockaddr_in)); // 初始化,置0
  43.     addr.sin_family=AF_INET; // Internet
  44.     addr.sin_port=htons(SERVER_PORT);// (將本機器上的short數據轉化爲網絡上的short數據)端口號
  45.     if(inet_aton(argv[1],&addr.sin_addr)<0) /*inet_aton函數用於把字符串型的IP地址轉化成網絡2進制數字*/ 
  46.     { 
  47.         fprintf(stderr,"Ip error:%s\n",strerror(errno)); 
  48.         exit(1); 
  49.     } 

  50.     udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in)); // 進行讀寫操作
  51.     close(sockfd); 
  52. }

編譯udp_server.c和udp_client.c

gcc udp_server.c -o udp_server

gcc udp_client.c -o udp_client

運行udp服務器段程序和客戶端程序,顯示過程截圖如下:


發佈了12 篇原創文章 · 獲贊 19 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章