Linux 下socket通訊終極指南(附TCP、UDP完整代碼)

來源:互聯網
上載者:User

linux下用socket通訊,有TCP、UDP兩種協議,網上的很多教程把兩個混在了一起,或者只講其中一種。現在我把自己這兩天研究的成果匯總下來,寫了一個完整的,適合初學者參考,也方便自己以後查閱。

首先講什麼是socket,不喜歡理論的可以略過。

Berkeley通訊端應用程式介面(API)包括了一個用C語言寫成的應用程式開發庫,主要用於實現進程間通訊,在電腦網路通訊方面被廣泛使用。(來自 wikipedia socket )

下面介紹一下常用的socket API(也來自 wikipedia socket)

 

這個列表是一個Berkeley通訊端API庫提供的函數或者方法的概要:

  • socket() 建立一個新的確定類型的通訊端,類型用一個整型數值標識,並為它分配系統資源。
  • bind() 一般用於伺服器端,將一個通訊端與一個通訊端地址結構相關聯,比如,一個指定的本地連接埠和IP地址。
  • listen() 用於伺服器端,使一個綁定的TCP通訊端進入監聽狀態。
  • connect() 用於用戶端,為一個通訊端分配一個自由的本地連接埠號碼。 如果是TCP通訊端的話,它會試圖獲得一個新的TCP串連。
  • accept() 用於伺服器端。 它接受一個從遠端用戶端發出的建立一個新的TCP串連的接入請求,建立一個新的通訊端,與該串連相應的通訊端地址相關聯。
  • send()recv(),或者write()read(),或者recvfrom()sendto(), 用於往/從遠程通訊端發送和接受資料。
  • close() 用於系統釋放分配給一個通訊端的資源。 如果是TCP,串連會被中斷。
  • gethostbyname()gethostbyaddr() 用於解析主機名稱和地址。
  • select() 用於修整有如下情況的通訊端列表: 準備讀,準備寫或者是有錯誤。
  • poll() 用於檢查通訊端的狀態。 通訊端可以被測試,看是否可以寫入、讀取或是有錯誤。
  • getsockopt() 用於查詢指定的通訊端一個特定的通訊端選項的當前值。
  • setsockopt() 用於為指定的通訊端設定一個特定的通訊端選項。

更多的細節如下給出。

[編輯]socket()

socket() 為通訊建立一個端點,為通訊端返回一個檔案描述符。 socket() 有三個參數:

  • domain 為建立的通訊端指定協議集。 例如:

    • PF_INET 表示IPv4網路通訊協定
    • PF_INET6 表示IPv6
    • PF_UNIX 表示本地通訊端(使用一個檔案)
  • type 如下:
    • SOCK_STREAM (可靠的面向流服務或流通訊端)
    • SOCK_DGRAM (資料報文服務或者資料報文通訊端)
    • SOCK_SEQPACKET (可靠的連續資料包服務)
    • SOCK_RAW (在網路層之上的原始協議)。
  • protocol 指定實際使用的傳輸協議。 最常見的就是IPPROTO_TCPIPPROTO_SCTPIPPROTO_UDPIPPROTO_DCCP。這些協議都在<netinet/in.h>中有詳細說明。 如果該項為“0”的話,即根據選定的domain和type選擇使用預設協議。

如果發生錯誤,函數傳回值為-1。 否則,函數會返回一個代表新分配的描述符的整數。

原型:
int socket(int domain, int type, int protocol)。
[編輯]bind()

bind() 為一個通訊端分配地址。當使用socket()建立通訊端後,只賦予其所使用的協議,並未分配地址。在接受其它主機的串連前,必須先調用bind()為通訊端分配一個地址。bind()有三個參數:

  • sockfd, 表示使用bind函數的通訊端描述符
  • my_addr, 指向sockaddr結構(用於表示所分配地址)的指標
  • addrlen, 用socklen_t欄位指定了sockaddr結構的長度

如果發生錯誤,函數傳回值為-1,否則為0。

原型
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
[編輯]listen()

當socket和一個地址綁定之後,listen()函數會開始監聽可能的串連請求。然而,這隻能在有可靠資料流保證的時候使用,例如:資料類型(SOCK_STREAM,SOCK_SEQPACKET)。

listen()函數需要兩個參數:

  • sockfd, 一個socket的描述符.
  • backlog, 一個決定監聽隊列大小的整數,當有一個串連請求到來,就會進入此監聽隊列,當隊列滿後,新的串連請求會返回錯誤。當請求被接受,返回 0。反之,錯誤返回 -1。

原型:

int listen(int sockfd, int backlog);
[編輯]accept()

當應用程式監聽來自其他主機的面對資料流的串連時,通過事件(比如Unix select()系統調用)通知它。必須用 accept()函數初始化串連。 Accept() 為每個串連創立新的通訊端並從監聽隊列中移除這個串連。它使用如下參數:

  • sockfd,監聽的通訊端描述符
  • cliaddr, 指向sockaddr 結構體的指標,客戶機地址資訊。
  • addrlen,指向 socklen_t的指標,確定客戶機地址結構體的大小 。

返回新的通訊端描述符,出錯返回-1。進一步的通訊必須通過這個通訊端。

Datagram 通訊端不要求用accept()處理,因為接收方可能用監聽通訊端立即處理這個請求。

函數原型:
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
[編輯]connect()

connect()系統調用為一個通訊端設定串連,參數有檔案描述符和主機地址。

某些類型的通訊端是不需連線的,大多數是UDP協議。對於這些通訊端,串連時這樣的:預設發送和接收資料的主機由給定的地址確定,可以使用 send()和 recv()。 返回-1表示出錯,0表示成功。

函數原型:
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

 

TCP socket通訊

伺服器端流程如下:

1.建立serverSocket

2.初始化 serverAddr(伺服器位址)

3.將socket和serverAddr 綁定 bind

4.開始監聽 listen

5.進入while迴圈,不斷的accept接入的用戶端socket,進行讀寫操作write和read

6.關閉serverSocket

用戶端流程:

1.建立clientSocket

2.初始化 serverAddr

3.連結到伺服器 connect

4.利用write和read 進行讀寫操作

5.關閉clientSocket

具體實現代碼如下

 

#server.c(TCP)
#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <errno.h>#define SRVPORT 10005#define CONNECT_NUM 5#define MAX_NUM 80int main(){    int serverSock=-1,clientSock=-1;    struct sockaddr_in serverAddr;    serverSock=socket(AF_INET,SOCK_STREAM,0);    if(serverSock<0)    {        printf("socket creation failed\n");        exit(-1);    }    printf("socket create successfully.\n");    memset(&serverAddr,0,sizeof(serverAddr));    serverAddr.sin_family=AF_INET;    serverAddr.sin_port=htons((u_short) SRVPORT);    serverAddr.sin_addr.s_addr=htons(INADDR_ANY);    if(bind(serverSock,&serverAddr,sizeof(struct sockaddr_in))==-1)    {        printf("Bind error.\n");        exit(-1);    }    printf("Bind successful.\n");    if(listen(serverSock,10)==-1)    {        printf("Listen error!\n");    }    printf("Start to listen!\n");    char revBuf[MAX_NUM]={0};    char sedBuf[MAX_NUM]={0};    while(1)    {        clientSock=accept(serverSock,NULL,NULL);        while(1)        {            if(read(clientSock,revBuf,MAX_NUM)==-1)            {                printf("read error.\n");            }            else            {                printf("Client:%s\n",revBuf);            }            if(strcmp(revBuf,"Quit")==0||strcmp(revBuf,"quit")==0)            {                strcpy(sedBuf,"Goodbye,my dear client!");            }            else            {                strcpy(sedBuf,"Hello Client.");            }            if(write(clientSock,sedBuf,sizeof(sedBuf))==-1)            {                printf("Send error!\n");            }            printf("Me(Server):%s\n",sedBuf);            if(strcmp(revBuf,"Quit")==0||strcmp(revBuf,"quit")==0)            {                break;            }            bzero(revBuf,sizeof(revBuf));            bzero(sedBuf,sizeof(sedBuf));        }        close(clientSock);    }    close(serverSock);    return 0;}
#client.c(TCP)#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <errno.h>#include <string.h>#define SRVPORT 10005#define CONNECT_NUM 5#define MAX_NUM 80int main(){    int clientSock=-1;    struct sockaddr_in serverAddr;    clientSock=socket(AF_INET,SOCK_STREAM,0);    if(clientSock<0)    {        printf("socket creation failed\n");        exit(-1);    }    printf("socket create successfully.\n");    memset(&serverAddr,0,sizeof(serverAddr));    serverAddr.sin_family=AF_INET;    serverAddr.sin_port=htons((u_short) SRVPORT);    serverAddr.sin_addr.s_addr=htons(INADDR_ANY);    if(connect(clientSock,&serverAddr,sizeof(struct sockaddr_in))<0)    {        printf("Connect error.\n");        exit(-1);    }    printf("Connect successful.\n");    char sedBuf[MAX_NUM]={0};    char revBuf[MAX_NUM]={0};    while(gets(sedBuf)!=-1)    {        if(write(clientSock,sedBuf,strlen(sedBuf))==-1)        {            printf("send error!\n");        }        printf("Me(Client):%s\n",sedBuf);        bzero(sedBuf,sizeof(sedBuf));        if(read(clientSock,revBuf,MAX_NUM)==-1)        {            printf("rev error!\n");        }        printf("Sever:%s\n",revBuf);        if(strcmp(revBuf,"Goodbye,my dear client!")==0)            break;        bzero(revBuf,sizeof(revBuf));    }    close(clientSock);    return 0;}

UDP協議不能保證資料通訊的可靠性,但是開銷更低,編起來也更加簡單

 

伺服器流程:

1.建立serverSocket

2.設定伺服器位址 serverAddr

3.將serverSocket和serverAddr綁定 bind

4.開始進行讀寫 sendto和recvfrom

5.關閉serverSocket

用戶端流程

1.建立clientSocket

2.設定伺服器位址 serverAddr

3.可選 設定clientAddr並和clientSocket(一般不用綁定)

4.進行發送操作 sendto

5.關閉clientSocket

具體代碼如下:

 

#server.c(UDP)#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <netinet/in.h>//for sockaddr_in#include <arpa/inet.h>//for socketint main(){ int fd=socket(AF_INET,SOCK_DGRAM,0); if(fd==-1) { perror("socket create error!\n"); exit(-1); } printf("socket fd=%d\n",fd); struct sockaddr_in addr; addr.sin_family=AF_INET; addr.sin_port=htons(6666); addr.sin_addr.s_addr=inet_addr("127.0.0.1"); int r; r=bind(fd,(struct sockaddr*)&addr,sizeof(addr)); if(r==-1) { printf("Bind error!\n"); close(fd); exit(-1); } printf("Bind successfully.\n");  char buf[255]; struct sockaddr_in from; socklen_t len; len=sizeof(from); while(1) { r=recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&from,&len); if(r>0) { buf[r]=0; printf("The message received for %s is :%s\n",inet_ntoa(from.sin_addr),buf); } else { break; } } close(fd); return 0;}

 

#client.c(UDP)#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <netinet/in.h>//for sockaddr_in#include <arpa/inet.h>//for socket int main(){ int fd=socket(AF_INET,SOCK_DGRAM,0); if(fd==-1) { perror("socket create error!\n"); exit(-1); } printf("socket fd=%d\n",fd); struct sockaddr_in addr_to;//目標伺服器地址 addr_to.sin_family=AF_INET; addr_to.sin_port=htons(6666); addr_to.sin_addr.s_addr=inet_addr("127.0.0.1");  struct sockaddr_in addr_from; addr_from.sin_family=AF_INET; addr_from.sin_port=htons(0);//獲得任意空閑連接埠 addr_from.sin_addr.s_addr=htons(INADDR_ANY);//獲得本機地址 r=bind(fd,(struct sockaddr*)&addr_from,sizeof(addr_from));int r;  if(r==-1) { printf("Bind error!\n"); close(fd); exit(-1); } printf("Bind successfully.\n"); char buf[255]; int len; while(1) { r=read(0,buf,sizeof(buf));  if(r<0) { break; } len=sendto(fd,buf,r,0,(struct sockaddr*)&addr_to,sizeof(addr_to));  if(len==-1) { printf("send falure!\n"); } else { printf("%d bytes have been sended successfully!\n",len); } } close(fd); return 0;}

以上代碼均經過測試(Ubuntu12.04),可以運行。有疑問,可以發電郵到ladd.cn@gmail.com

 

參考文章

1.wikipedia socket http://zh.wikipedia.org/wiki/Socket

2.TCP socket 之linux實現http://os.51cto.com/art/201001/179878.htm

 本文由ladd原創,歡迎轉載,但請註明出處:

http://www.cnblogs.com/ladd/archive/2012/06/25/2560888.html 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.