C語言中文網 目錄
首頁 > socket 閱讀:2,800

Linux下的socket演示程序

C語言教程一樣,我們從一個簡單的“Hello World!”程序切入 socket 編程。

本節演示了 Linux 下的代碼,server.cpp 是服務器端代碼,client.cpp 是客戶端代碼,要實現的功能是:客戶端從服務器讀取一個字符串并打印出來。

服務器端代碼 server.cpp:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

int main(){
    //創建套接字
    int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    //將套接字和IP、端口綁定
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每個字節都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具體的IP地址
    serv_addr.sin_port = htons(1234);  //端口
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    //進入監聽狀態,等待用戶發起請求
    listen(serv_sock, 20);

    //接收客戶端請求
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size = sizeof(clnt_addr);
    int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);

    //向客戶端發送數據
    char str[] = "http://www.frifeb11.com/socket/";
    write(clnt_sock, str, sizeof(str));
   
    //關閉套接字
    close(clnt_sock);
    close(serv_sock);

    return 0;
}

客戶端代碼 client.cpp:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main(){
    //創建套接字
    int sock = socket(AF_INET, SOCK_STREAM, 0);

    //向服務器(特定的IP和端口)發起請求
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));  //每個字節都用0填充
    serv_addr.sin_family = AF_INET;  //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具體的IP地址
    serv_addr.sin_port = htons(1234);  //端口
    connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
   
    //讀取服務器傳回的數據
    char buffer[40];
    read(sock, buffer, sizeof(buffer)-1);
   
    printf("Message form server: %s\n", buffer);
   
    //關閉套接字
    close(sock);

    return 0;
}

啟動一個終端(Shell),先編譯 server.cpp 并運行:

[admin@localhost ~]$ g++ server.cpp -o server
[admin@localhost ~]$ ./server
#等待請求的到來

正常情況下,程序運行到 accept() 函數就會被阻塞,等待客戶端發起請求。

接下再啟動一個終端,編譯 client.cpp 并運行:

[admin@localhost ~]$ g++ client.cpp -o client
[admin@localhost ~]$ ./client
Message form server: http://www.frifeb11.com/socket/

client 接收到從 server發送過來的字符串就運行結束了,同時,server 完成發送字符串的任務也運行結束了。大家可以通過兩個打開的終端來觀察。

client 運行后,通過 connect() 函數向 server 發起請求,處于監聽狀態的 server 被激活,執行 accept() 函數,接受客戶端的請求,然后執行 write() 函數向 client 傳回數據。client 接收到傳回的數據后,connect() 就運行結束了,然后使用 read() 將數據讀取出來。
server 只接受一次 client 請求,當 server 向 client 傳回數據后,程序就運行結束了。如果想再次接收到服務器的數據,必須再次運行 server,所以這是一個非常簡陋的 socket 程序,不能夠一直接受客戶端的請求。

源碼解析

1) 先說一下 server.cpp 中的代碼。

第 11 行通過 socket() 函數創建了一個套接字,參數 AF_INET 表示使用 IPv4 地址,SOCK_STREAM 表示使用面向連接的套接字,IPPROTO_TCP 表示使用 TCP 協議。在 Linux 中,socket 也是一種文件,有文件描述符,可以使用 write() / read() 函數進行 I/O 操作,這一點已在《socket是什么》中進行了講解。

第 19 行通過 bind() 函數將套接字 serv_sock 與特定的 IP 地址和端口綁定,IP 地址和端口都保存在 sockaddr_in 結構體中。

socket() 函數確定了套接字的各種屬性,bind() 函數讓套接字與特定的IP地址和端口對應起來,這樣客戶端才能連接到該套接字。

第 22 行讓套接字處于被動監聽狀態。所謂被動監聽,是指套接字一直處于“睡眠”中,直到客戶端發起請求才會被“喚醒”。

第 27 行的 accept() 函數用來接收客戶端的請求。程序一旦執行到 accept() 就會被阻塞(暫停運行),直到客戶端發起請求。

第 31 行的 write() 函數用來向套接字文件中寫入數據,也就是向客戶端發送數據。

和普通文件一樣,socket 在使用完畢后也要用 close() 關閉。

2) 再說一下 client.cpp 中的代碼。client.cpp 中的代碼和 server.cpp 中有一些區別。

第 19 行代碼通過 connect() 向服務器發起請求,服務器的IP地址和端口號保存在 sockaddr_in 結構體中。直到服務器傳回數據后,connect() 才運行結束。

第 23 行代碼通過 read() 從套接字文件中讀取數據。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo