linux c編程:網絡編程


在網絡上,通信服務都是采用C/S機制,也就是客戶端/服務器機制。流程可以參考下圖:

服務器端工作流程:

使用socket()函數創建服務器端通信套接口

使用bind()函數將創建的套接口與服務器地址綁定

使用listen()函數使服務器套接口做好接收連接請求准備

使用accept()接收來自客戶端由connect()函數發出的連接請求

根據連接請求建立連接后,使用send()函數發送數據,或者使用recv()函數接收數據

使用closesocket()函數關閉套接口(可以先用shutdown()函數先關閉讀寫通道)



客戶端程序工作流程:

使用socket()函數創建客戶端套接口

使用connect()函數發出也服務器建立連接的請求(調用前可以不用bind()端口號,由系統自動完成)

連接建立后使用send()函數發送數據,或使用recv()函數接收數據

使用closesocet()函數關閉套接口

下面介紹幾個函數的用法:

socket函數:int socket(int domain,int type,int protocol)

參數說明:
domain:
指明協議族,也稱為協議域,是一個常值。
AF_INET:IPv4
協議
AF_INET6:IPv6
協議
AF_LOCAL/AF_UNIX:Unix
協議域
AF_ROUTE:
路由套接字
AF_KE:
密匙套接字

type
:指明套接字的類型。
SOCK_STREA:
字節流套接字
SOCK_DGRA:
數據報套接字
SOCK_SEQPACKE:
有序分組套接字
SOCK_RAW:
原始套接字

protocol:
指明協議類型。一般為0,以選擇給定的domaintype組合的系統默認值。
IPPROTO_TCP:TCP
傳輸協議
IPPROTO_UDP:UDP
傳輸協議
IPPROTO_SCTP:SCTP
傳輸協議

函數描述:
socket
函數在成功時返回一個小的非負整數值,與文件描述符類似,我們稱它為套接字
描述符,簡稱 sockfd。為了得到這個套接字描述符,我們只是指定了協議族(IPv4IPv6
Unix)和套接字類型(字節流、數據報或原始套接字)。我們並沒有指定本地跟遠程的
協議地址

bind函數:int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

將一個本地協議地址賦予一個套接字。對於網際網協議,協議地址是32位的IPv4地址和128
位的IPv6地址與16位的TCPUDP端口號的組合。bind 函數主要用於服務器端,用來指定本地
主機的哪個網絡接口(IP,可以是INADDR_ANY,表示本地主機的任一網絡接口)可以接受客戶
端的請求,和指定端口號(即開啟的等待客戶來連接的進程)。

參數說明:

sockfd: socket 函數返回的套接字描述符。

myaddraddrlen:指向一個套接字地址結構的指針和該結構的大小。

地址結構的一般采用sockadr_in結構體

struct sockaddr_in{

short int sin_family; #地址族

unsigned short int sin_port; #端口號

struct n_addr sin_addr; #IP地址

unsigned char sin_zeor[8]; #填充0保持與struct sockaddr同樣大小

}

 

listen函數:

int listen(int sockfd,int backlog)

listen函數的第一個參數即為要監聽的socket描述字,第二個參數為相應socket可以排隊的最大連接個數。socket()函數創建的socket默認是一個主動類型的,listen函數將socket變為被動類型的,等待客戶的連接請求。

Connetc函數:

connect函數的第一個參數即為客戶端的socket描述字,第二參數為服務器的socket地址,第三個參數為socket地址的長度。客戶端通過調用connect函數來建立與TCP服務器的連接



accept函數:

int accept(int sockfd,struct sockaddr *addr, socketen_t *add_len)

參數 sockfd
參數 sockfd 就是上面解釋中的監聽套接字,這個套接字用來監聽一個端口,當有一個客戶與服務器連接時,它使用這個一個端口號,而此時這個端口號正與這個套接字關聯。當然客戶不知道套接字這些細節,它只知道一個地址和一個端口號。
參數 addr
這是一個結果參數,它用來接受一個返回值,這返回值指定客戶端的地址,當然這個地址是通過某個地址結構來描述的,用戶應該知道這一個什么樣的地址結構。如果對客戶的地址不感興趣,那么可以把這個值設置為 NULL
參數 len
如同大家所認為的,它也是結果的參數,用來接受上述 addr 的結構的大小的,它指明 addr 結構所占有的字節個數。同樣的,它也可以被設置為 NULL

如果accept成功返回,則服務器與客戶已經正確建立連接了,此時服務器通過accept返回的套接字來完成與客戶的通信。

服務器測的代碼如下:對於服務端來說,首先是建立socket然后綁定IP地址到socket上。然后開始監聽。

當接收到請求的時候,通過recv函數存儲在buf數組里面。並且通過send函數向對端發送消息

int server_function()
{
    char *sendbuf="thanks";
    char buf[256];
    int s_fd,c_fd;
    int s_len,c_len;
    struct sockaddr_in s_addr;
    struct sockaddr_in c_addr;
    s_fd=socket(AF_INET,SOCK_STREAM,0);
    s_addr.sin_family=AF_INET;
    s_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    s_addr.sin_port=PORT;
    s_len=sizeof(s_addr);
    bind(s_fd,(struct sockaddr *)&s_addr,s_len);
    listen(s_fd,10);
    while(1){
        printf("please wait a moment\n");
        c_len=sizeof(c_addr);
        c_fd=accept(s_fd,(struct sockaddr *)&c_addr,(socklen_t *__restrict)&c_len);
        recv(c_fd,buf,256,0);
        buf[strlen(buf)+1]='\0';
        printf("receve message:\n%s\n",buf);
        send(c_fd,sendbuf,strlen(sendbuf),0);
        close(c_fd);
    }
}

 

 

客戶端的代碼如下:設置好連接的IP地址和端口后,通過connect發起連接。並通過sendrecv函數進行發送和接收消息

int client_function()
{
    char *buf="come on";
    char rebuf[250];
    int sockfd,len,newsockfd,len2;
    struct sockaddr_in addr;
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    addr.sin_family=AF_INET;
    addr.sin_addr.s_addr=htonl(INADDR_ANY);
    addr.sin_port=PORT;
    len=sizeof(addr);
    newsockfd=connect(sockfd,(struct sockaddr *)&addr,len);
    len2=strlen(buf);
    send(sockfd,buf,len2,0);
    sleep(5);
    recv(sockfd,rebuf,40,0);
    printf("the length of the rebuf is %d",strlen(rebuf));
    rebuf[strlen(rebuf)+1]='\0';
    printf("receive message:\n%s\n",rebuf);
    close(sockfd);
    return 0;
}

 

打開2個終端,分別運行服務器和客戶端的代碼。執行結果如下:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM