Linux網絡編程基礎API

一、套接字的地址結構
1)IPv4套接字:

#include<netinet/in.h>
struct sockaddr_in
{
    sa_family_t sin_family;     //地址族,AF_INET
    u_int16_t sin_port;         //端口號,用網絡字節表示
    Struct in_addr sin_addr;    //IPv4地址結構,如下
};
struct in_addr
{
    u_int32_t s_addr;           //IPv4地址,用網絡字節序表示
};

2)IPv6:

struct sockaddr_in6
{
    sa_family_t sin6_family;     //地址族,AF_INET6
    u_int16 sin6_port;           //端口號,用網絡字節表示
    u_int32_t sin6_flowinfo;     //信息流,應設置爲0
    struct in6_addr sin6_addr;   //IPv6地質結構體,如下
    u_int32_t sin6_scope_id;     //scope ID,現處實驗階段
};
struct in6_addr
{
    unsigned cahr sa_addr[16];   //IPv6地址,用網絡字節序表示
};

二、IP地址轉換函數
由於人們喜歡用可讀性好的字符串來表示IP地址,用點分十進制字符串表示IPv4,用十六進制字符串表示IPv6地址。但在網絡中應把它們轉化爲二進制使用。而記錄日誌相反。以下三個函數可用於相互之間的轉換:

#include<arpa/inet.h>
in_addr_t inet_addr(const char* strptr); //IPv4地址字符串->二進制
int inet_aton(const char* cp, struct in_addr* inp); //同上,結果存於inp指向的地址結構中
cahr* inet_ntoa(struct in_addr in); //網絡字節->點分十進制(內部用靜態變量存儲轉化結果,因此具有不可重入性)

下面兩個更新的函數對IPv4和IPv6都可使用:

#include<arpa/inet.h>
int inet_pton(int af, const char* src, void* dst);
const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt);

上面的af指定地址族,dst指定結果存儲的內存,cnt指定目標存儲單元的大小。
下面兩個宏定義了cnt的大小:

#include<netinet/in.h>
#define INET_ADDRESTRLEN 16
#define INET6_ADDRESTRLEN 46

三、創建socket
UNIX/Linux中:所有東西都是文件。socket也不例外,它就是可讀、可寫、可控制、可關閉的文件描述符。下面socket系統調用創建一個socket:

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
//             協議族  服務類型(流或數) 幾乎默認爲0

成功時創建一個文件描述符,失敗返回-1,設置爲errno。

四、命名socket:講一個socket與socket地址綁定成爲給socket命名。

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);

bind成功時返回0,失敗時返回-1,並設置errno。其中兩個常見的errno爲:
*EACCES,被綁定的地址受保護,僅超級用戶能夠訪問。如普通用戶將socket綁定到知名服務端口(0~1023)時,返回EACCES。
*EADDRINUSE,被綁定的地址正在使用中。比如把socket綁定到一個處於TIME_WAIT狀態的socket。

五、監聽socket
調用一個監聽隊列存放待處理的客戶連接:

#include<sys/socket.h>
int listen(int sockfd, int backlog);
//                       監聽隊列的最大長度(典型參數爲5)

六、接受連接

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
//                         獲取被接受連接的遠端socket地址    接收地址長度參數
accept只是從監聽隊列中取出連接,而不論連接處於何種狀態,更不關心網絡的變化。

七、發起連接

#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen);

八、關閉連接
關閉一個連接實際上就是關閉該連接對應的socket。

#include<unistd.h>
int close(int fd);  //fd->待關閉的socket
close系統調用並不非是立即關閉一個連接,而是將fd的引用計數減1,只有當fd的引用計數爲0時,才真正關閉連接。

當無論如何都要立即終止連接,可以使用shutdown調用:
#include<sys/socket.h>
#int shutdown(int sockfd, int howto);

九、數據讀寫
對文件的讀寫操作read和write同樣適用於socket。但socket的讀寫增加了對數據控制。用於TCP流數據讀寫的系統調用是:
ssize_t爲:sign size_t類型的,可以被執行讀寫操作的數據塊的大小。

            (1)發送帶外數據
#include<sys/types.h>
#include<sys/socket.h>
//                            位置         大小       默認0
ssize_t recv(int sockfd, void* buf, size_t len, int flags);
ssize_t send(int sockfd, const void* buf, size_t len, int flags);

幾個重要的flags參數的可選值:
MSG_OOB:發送或接收緊急數據(提供帶外數據的發送和接收)。

下面兩個代碼一個爲發送帶外數據,一個爲接收帶外數據。

#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>

int main(int argc, char* argv[])
{
    int t = 1;
    if(argc<=2)
    {
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in server_address;
    bzero(&server_address, sizeof(server_address));
    server_address.sin_family = AF_INET;

    inet_pton(AF_INET, ip, &server_address.sin_addr);
    server_address.sin_port = htons(port);

    int sockfd = socket(PF_INET, SOCK_STREAM,0);
    assert(sockfd >=0);
    if(connect(sockfd,(struct sockaddr*)&server_address,sizeof(server_address))<0)
    {
        printf("connection failed\n");
    }
    else
    {
        const char* oob_data = "wer";
        const char* normal_data = "987";
        send(sockfd, normal_data,strlen(normal_data), 0);
        send(sockfd, oob_data,strlen(oob_data),MSG_OOB);
        send(sockfd, normal_data,strlen(normal_data), 0);
    scanf("%d\n", t);
    }   

    close(sockfd);
    return 0;

}
                (2)接收帶外數據
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<errno.h>

#define BUF_SIZE 1024

int main(int argc, char* argv[])
{
    if(argc<=2)
    {
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in address;
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_port = htons(port);

    int sock = socket(PF_INET,SOCK_STREAM,0);
    assert(sock>=0);

    int ret = bind(sock,(struct sockaddr*)&address, sizeof(address));
    assert(ret !=-1);

    ret = listen(sock,5);
    assert(ret !=-1);

    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof(client);
    int connfd = accept(sock, (struct sockaddr*)&client,&client_addrlength);
    if(connfd<0)
    {
        printf("errno is: %d\n",errno);
    }

    else
    {
        char buffer[BUF_SIZE];

        memset(buffer,'\0',BUF_SIZE);
        ret = recv(connfd, buffer, BUF_SIZE-1, 0);
        printf("got %d bytes of normal data '%s'\n", ret, buffer);

        memset(buffer, '\0',BUF_SIZE);
        ret = recv(connfd,buffer,BUF_SIZE-1,MSG_OOB);
        printf("got %d bytes of oob data '%s'\n", ret, buffer);

        memset(buffer, '\0',BUF_SIZE);
        ret = recv(connfd, buffer, BUF_SIZE-1, 0);
        printf("got %d bytes of normal data '%s'\n", ret, buffer);

        close(connfd);

    }

    close(sock);
    return 0;
}
發佈了47 篇原創文章 · 獲贊 24 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章