Linux下簡單的socket通訊實例

Linux下簡單的socket通訊實例html

 

 If you spend too much time thinking about a thing, you’ll never get it done.git

                                                                                                                                      —Bruce Leegithub

      學習網絡編程也一段時間了,剛開始看《UNIX網絡編程》的時候,以爲這本厚厚的書好難啊!看到後來,發現並無想象中的那麼難。若是你是新手,建議你看到第二部分結束後,開始着手寫代碼。不寫代碼確定是不行的。看100遍也沒有敲一遍實現一遍來的清楚。敲完之後,帶着問題去看書,你會更加有針對性。提升的速度是飛快的,這也是學習任何一本書、一門語言的惟一手段。編程

  寫這個博客也是由於剛開始學的時候,查了好多別人寫的東西,百度了之後,發現你們只是把全部的代碼一貼。並無講解每一個函數的功能。我甚至不知道哪一個函數是哪一個頭文件下的。形成我對函數很不理解。下面我會對每一個函數的功能,和它的頭文件以及函數原型寫出來,讓你們參考,第一次寫博客,有什麼錯誤的地方,但願你們指正。能夠在下面給我留言,也是我繼續寫下去的動力。服務器

     我很但願和你們一塊兒分享學習網絡編程遇到的種種困難與不順,也但願和你們一塊兒討論其中遇到的問題,一塊兒成長,若是你剛開始打算學習網絡編程,那這篇文章必定能給你一些幫助。網絡

  個人郵箱:cvmimi_linhai@foxmail.com,轉載請註明出處:http://www.cnblogs.com/yusenwu/p/4579167.html。socket

     關於怎樣介紹這個簡單的實例:(基本上涵蓋了《UNIX網絡編程》1-5章的內容,更深,更細的,須要咱們再細讀這本書函數

      --> 一、代碼展現,功能介紹學習

  --> 二、首先介紹一下客戶端和服務端中函數的功能以及函數的原形。網站

  --> 三、關於鏈接三次握手和TCP鏈接關閉時候的分組交換

      --> 四、IPv四、IPv6套接字的地址結構

  --> 五、一些好的學習網站總結

  --> 六、代碼下載

  --> 七、總結

  --> 八、實現一個echo的實例,代碼能夠到Github上下載

client.c

 1  #include <stdio.h>
 2  #include <sys/socket.h>
 3  #include <sys/types.h>
 4  #include <stdlib.h>
 5  #include <netinet/in.h>
 6  #include <errno.h>
 7  #include <string.h>
 8  #include <arpa/inet.h>
 9  #include <unistd.h>
10  #define MAXLINE 1024
11  int main(int argc,char **argv)
12  {
13  char *servInetAddr = "127.0.0.1";
14  int socketfd;
15  struct sockaddr_in sockaddr;
16  char recvline[MAXLINE], sendline[MAXLINE];
17  int n;
18  
19  if(argc != 2)
20  {
21  printf("client <ipaddress> \n");
22  exit(0);
23  }
24  
25  socketfd = socket(AF_INET,SOCK_STREAM,0);
26  memset(&sockaddr,0,sizeof(sockaddr));
27  sockaddr.sin_family = AF_INET;
28  sockaddr.sin_port = htons(10004);
29  inet_pton(AF_INET,servInetAddr,&sockaddr.sin_addr)
30 if((connect(socketfd,(struct sockaddr*)&sockaddr,sizeof(sockaddr))) < 0 )
31 { 31 printf("connect error %s errno: %d\n",strerror(errno),errno); 32 exit(0); 33 } 34 35 printf("send message to server\n"); 36 37 fgets(sendline,1024,stdin); 38 39 if((send(socketfd,sendline,strlen(sendline),0)) < 0) 40 { 41 printf("send mes error: %s errno : %d",strerror(errno),errno); 42 exit(0); 43 } 44 45 close(socketfd); 46 printf("exit\n"); 47 exit(0); 48 }

-執行:gcc client.c -o client    後啓動 ./client 客戶端程序 啓動前先啓動./server-----------------------------------------

 server.c

 1  #include <stdio.h>
 2  #include <sys/socket.h>
 3  #include <sys/types.h>
 4  #include <string.h>
 5  #include <netinet/in.h>
 6  #include <stdlib.h>
 7  #include <errno.h>
 8  #include <unistd.h>
 9  #include <arpa/inet.h>
10  
11  #define MAXLINE 1024
12  int main(int argc,char **argv)
13  {
14  int listenfd,connfd;
15  struct sockaddr_in sockaddr;
16  char buff[MAXLINE];
17  int n;
18  
19  memset(&sockaddr,0,sizeof(sockaddr));
20  
21  sockaddr.sin_family = AF_INET;
22  sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
23  sockaddr.sin_port = htons(10004);
24  
25  listenfd = socket(AF_INET,SOCK_STREAM,0);
26  
27  bind(listenfd,(struct sockaddr *) &sockaddr,sizeof(sockaddr));
28  
29  listen(listenfd,1024);
30 
31  
32  printf("Please wait for the client information\n");
33  
34  for(;;)
35  {
36  if((connfd = accept(listenfd,(struct sockaddr*)NULL,NULL))==-1)
37  {
38  printf("accpet socket error: %s errno :%d\n",strerror(errno),errno);
39  continue;
40  }
41  
42  n = recv(connfd,buff,MAXLINE,0);
43  buff[n] = '\0';
44  printf("recv msg from client:%s",buff);
45  close(connfd);
46  }
47  close(listenfd);
48  }

-執行:gcc server.c -o server    後啓動 ./server 服務端程序-------------------------------------------------------

 

      > 一、代碼展現,功能介紹

   上面這個簡單的socket通訊的代碼要實現的功能:從客戶端發送一條消息後,服務端接收這條消息,並在服務端顯示(recv msg from client:****)。

   

  > 二、首先介紹一下客戶端和服務端中函數的功能以及函數的原形。 

#include <sys/socket.h> 
int socket(int family, int type, int protocol);   //指按期望的通訊協議類型,返回的文件描述符和套接字描述符相似,咱們成爲套接字描述符,簡稱sockfd  

 family:協議族

family 說明
AF_INET IPv4協議 
AF_INET6 IPv6
AF_LOCAL Unix域協議(15章)
AF_ROUTE  路由套接字(18章)
AF_KEY 密鑰套接字(19章)

 type:套接字的類型

type 說明
SOCK_STREAM(經常使用) 字節流套接字
SOCK_DGRAM 數據報套接字
SOCK_SEQPACKET  有序分組套接字
SOCK_RAW 原始套接字

 protocol:協議類型的常量或設置爲0,以選擇給定的family和type組合的系統默認值

protocol 說明
IPPROTO_TCP TCP傳輸協議
IPPROTO_UDP UDP傳輸協議
IPPROTO_SCTP SCTP傳輸協議

    

#include<arpa/inet.h>
int inet_pton(int family,const char *strptr,void *addrptr);//成功返回1,格式不對返回0,出錯返回-1
//做用:p表明表達式 n表明數值 之後所寫的全部代碼中都有可能會須要這個函數,因此這個函數很重要
//將char所指向的字符串,經過addrptr指針存放
//他的反函數: inet_ntop()做用相反。能夠百度查閱這個函數的功能。由於例子裏咱們沒有涉及到,就不介紹了。之後用到的時候再說
//須要注意的是:當他發生錯誤的時候,errno的值會被置爲EAFNOSUPPORT 關於errno值咱們一下子介紹。
#include <sys/socket.h> 
int connect(int sockfd,const struct sockaddr* servaddr,socklen_t addrlen);//用connect函數來創建與TCP服務器的鏈接
#include<unistd.h>
int close(int sockfd);//關閉socket,並終止TCP鏈接
#include <sys/socket.h>
int bind(int sockfd,const struct* myaddr,socklen_t addrlen);//把本地協議地址賦予一個套接字。也就是將32位的IPv4或128位ipv6與16位的TCP或者UDP組合。
#include<sys/socket.h>
int listen(int sockfd,int backlog)//成功返回0,失敗返回-1     listen函數僅由TCP服務器調用
//listen函數將會作兩件事:
//1:咱們在建立套接字的時候使用了socket函數,它建立的套接字是主動套接字,bind函數的功能就是經過這個將主動套接字,變成被動套接字,告訴內核應該接受指向這個套接字的請//求,CLOSED狀態變成LISTEN狀態
//2:本函數的第二個參數規定了內核要爲該套接字排隊的最大鏈接個數。
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr* cliaddr,socklen_t *addrlen);//成功返回描述符,失敗返回-1
//一、若是第二三個參數爲空,表明了,咱們對客戶的身份不感興趣,所以置爲NULL;
//二、第一個參數爲socket建立的監聽套接字,返回的是已鏈接套接字,兩個套接字是有區別的,並且很是重要。區別:咱們所建立的監聽套接字通常服務器只建立一個,而且一直存在。而內核會爲每個服務器進程的客戶鏈接創建一個鏈接套接字,當服務器完成對某個給定客戶的服務時,鏈接套接字就會被關閉。

  

      

  總結:咱們學校的實驗室是雲計算實驗室,有不少的集羣,我在上面開了2臺虛擬機,在兩臺Linux系統上跑。能夠成功接收。只要將IP設置好便可,注意,關掉防火牆:service iptables stop;

  > 三、關於鏈接三次握手和TCP鏈接關閉時候的分組交換

  三次握手:

  爲了更好的理解connect、bind、close三個函數,瞭解一下TCP鏈接的創建和終止是頗有必要的。(請務必理解理解上面的全部的函數後,再看這節)。

  一、服務器首先必須被打開,等待準備接受外來的鏈接。咱們上面的例子用到了socket、bind、listen這3個函數。以後,咱們稱爲服務端被被動打開了。

  二、客戶端是經過connect發起主動打開。

  

      三、主動打開後,客戶TCP發送了一個SYN(同步)分節,它告訴服務器客戶將在鏈接中只發送的數據的初始序列號,SYN分節不攜帶數據。它發送的IP數據報,只有一個IP首部、一個TCP首部以及TCP選項。

  四、服務器必須確認(ACK)客戶的SYN,同時本身也發送一個SYN分節,它含有服務器將在同一鏈接中發送的數據的初始序列號。服務器在單個分節中發送SYN和對客戶SYN的ACK確認(+1)。

  五、客戶必須確認服務器的SYN分節。

  上面的過程稱爲TCP的三次握手。

    注:SYN(synchronous)是TCP/IP創建鏈接時使用的握手信號。在客戶機和服務器之間創建正常的TCP網絡鏈接時,客戶機首先發出一個SYN消息,服務器使用SYN+ACK應答表示接收到了這個消息,最後客戶機再以ACK消息響應。這樣在客戶機和服務器之間才能創建起可靠的TCP鏈接,數據才能夠在客戶機和服務器之間傳遞

 

  TCP鏈接終止

  終止一個鏈接須要4個分節。

  一、經過調用close,咱們執行主動關閉,TCP發送一個FIN(finish,表示結束),表示數據發送完畢。

  二、對端接收到FIN後,執行被動關閉。

    三、一段時候後,接收到文件結束符的應用進程,將調用close關閉它的套接字。因而套接字也發送一個了FIN。

  四、確認這個FIN ACK+1   下圖很清楚的表達了。  

  五、咱們也稱它爲TCP四次握手。

  

      > 四、IPv四、IPv6套接字的地址結構

  IPv4地址結構: 

 1 struct in_addr {
 2    in_addr_t  s_addr;  
 3 };
 4 
 5 struct sockaddr_in {
 6    uint8_t sin_len; //無符號8位整型
 7    sa_family_t sin_famliy;  /*AF_INET*/
 8    in_port_t  sin_port;     
9 struct in_addr sin_addr; /*32位 IPv4 地址*/
10 char sin_zero[8]; /*unuse*/ 11 };
//頭文件 #include <sys/types.h>
//sa_family_t和socklen_t 頭文件 #include <sys/socket.h>
//in_addr_t in_port_t 頭文件 #include <netinet/in.h>

 

  IPv6地址結構:

struct in6_addr {
   uint8_t  s6_addr[16];  
};

#define SIN6_LEN

struct sockaddr_in6 {
   uint8_t sin6_len;
   sa_family_t sin6_famliy;
   in_port_t  sin6_port;

   uint32_t sin6_flowinfo;
   struct in6_addr sin6_addr;

   uint32_t sin6_scope_id;   
};

 

  > 五、一些好的學習網站總結

  一、關於51CTO上的這個視頻http://edu.51cto.com/course/course_id-903.html,我買了,可是講的很是爛,建議你們不要購買。教課的老師也就是照着書念,還不如本身。浪費錢。

  二、http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html 

       http://blog.csdn.net/hguisu/article/details/7445768/

     http://www.oschina.net/code/snippet_97047_675

           這幾篇博客不錯,能帶你入門。

  > 六、代碼下載

  Githubhttps://github.com/micwu/Demo

  > 七、總結

   學習之路是很蠻長的。想要學好,很是難,須要長期的積累。我也正在學習中。通過了不少的挫折,可是有理想,就必定能成功。但願你們想走Linux下服務器編程的同志們,一塊兒加油吧。

  > 八、echo實現

  

  代碼下載:Github

相關文章
相關標籤/搜索