1. 程式人生 > >Socket編程實踐(3) --Socket API

Socket編程實踐(3) --Socket API

address img silent rcv adb desc () 默認 cal

socket函數

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

創建一個套接字用於通信

參數:

domain:指定通信協議族(protocol family),經常使用取值AF_INET(IPv4)

type:指定socket類型, 流式套接字SOCK_STREAM。數據報套接字SOCK_DGRAM,原始套接字SOCK_RAW

protocol:協議類型,經常使用取值0, 使用默認協議

返回值:

成功: 返回非負整數,套接字;

失敗: 返回-1


bind函數

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

綁定一個本地地址到套接字

參數:

sockfd:socket函數返回的套接字

addr:要綁定的地址

//sockaddr_in結構, bind時須要強制轉換成為struct sockaddr*類型
struct sockaddr_in
{
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};
/* Internet address. */
struct in_addr
{
    uint32_t       s_addr;     /* address in network byte order */
};
/**演示樣例:INADDR_ANY的使用, 綁定本機隨意地址**/
int main()
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1)
        err_exit("socket error");

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8001);
    //綁定本機的隨意一個IP地址, 作用同以下兩行語句
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //inet_aton("127.0.0.1", &addr.sin_addr);
    //addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)
        err_exit("bind error");
    else
        cout << "bind success" << endl;
}

listen函數

int listen(int sockfd, int backlog);

listen函數應該用在調用socketbind函數之後, 而且用在調用accept之前, 用於將一個套接字從一個主動套接字轉變成為被動套接字。

backlog說明:

對於給定的監聽套接口,內核要維護兩個隊列:

1、已由客戶發出並到達服務器,服務器正在等待完畢對應的TCP三路握手過程(SYN_RCVD狀態)

2、已完畢連接的隊列(ESTABLISHED狀態)

可是兩個隊列長度之和不能超過backlog

技術分享

backlog推薦使用SOMAXCONN(3.13.0-44-generic中該值為128), 使用等待隊列的最大值;

Man-Page中的listen說明:

listen() marks the socket referred to by sockfd as a passive socket, that is, as a socket that

will be used to accept incoming connection requests using accept(2).

The sockfd argument is a file descriptor that refers to a socket of type SOCK_STREAM or

SOCK_SEQPACKET.

The backlog argument defines the maximum length to which the queue of pending connections for

sockfd may grow. If a connection request arrives when the queue is full, the client may receive

an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission,

the request may be ignored so that a later reattempt at connection succeeds.

技術分享

If the backlog argument is greater than the value in /proc/sys/net/core/somaxconn(Ubuntu 14.04 該值為128), then it is silently truncated to that value; the default value in this file is 128. In kernels

before 2.4.25, this limit was a hard coded value, SOMAXCONN, with the value 128.

accept函數

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

已完畢連接隊列返回第一個連接(the first connection request on the queue of pending connections for the listening

socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket.

The newly created socket is not in the listening state),假設已完畢連接隊列為空。則堵塞。The original

socket sockfd is unaffected by this call.

參數:

sockfd:服務器套接字

addr:將返回對等方的套接字地址, 不關心的話, 能夠設置為NULL

addrlen:返回對等方的套接字地址長度, 不關心的話能夠設置成為NULL, 否則一定要初始化

返回值:

On success, these system calls return a non-negative integer that is a descriptor for the accepted

socket. On error, -1 is returned, and errno is set appropriately.

connect函數

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

建立一個連接至addr所指定的套接字

參數:

sockfd:未連接套接字

addr:要連接的套接字地址

addrlen:第二個參數addr長度

演示樣例:echo server/client實現

//server端代碼
int main()
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1)
        err_exit("socket error");

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8001);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)
        err_exit("bind error");
    if (listen(listenfd, SOMAXCONN) == -1)
        err_exit("listen error");

    char buf[512];
    int readBytes;
    struct sockaddr_in clientAddr;
    //謹記: 此處一定要初始化
socklen_t addrLen = sizeof(clientAddr);
    while (true)
    {
        int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);
        if (clientfd == -1)
            err_exit("accept error");
        //打印客戶IP地址與端口號
        cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)
             << ", " << ntohs(clientAddr.sin_port) << endl;

        memset(buf, 0, sizeof(buf));
        while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0)
        {
            cout << buf;
            if (write(clientfd, buf, readBytes) == -1)
                err_exit("write socket error");
            memset(buf, 0, sizeof(buf));
        }
        if (readBytes == 0)
        {
            cerr << "client connect closed..." << endl;
            close(clientfd);
        }
        else if (readBytes == -1)
            err_exit("read socket error");
    }
    close(listenfd);
}
//client端代碼
int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
        err_exit("socket error");

    //填寫服務器端口號與IP地址
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(8001);
    serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if (connect(sockfd, (const struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)
        err_exit("connect error");

    char buf[512];
    while (fgets(buf, sizeof(buf), stdin) != NULL)
    {
        if (write(sockfd, buf, strlen(buf)) == -1)
            err_exit("write socket error");
        memset(buf, 0, sizeof(buf));
        int readBytes = read(sockfd, buf, sizeof(buf));
        if (readBytes == 0)
        {
            cerr << "server connect closed... \nexiting..." << endl;
            break;
        }
        else if (readBytes == -1)
            err_exit("read socket error");
        cout << buf;
        memset(buf, 0, sizeof(buf));
    }
    close(sockfd);
}

附-Makefile

.PHONY: clean all 
CC = g++ 
CPPFLAGS = -Wall -g -pthread -std=c++11
BIN = server client
SOURCES = $(BIN.=.cpp)

all: $(BIN)
$(BIN): $(SOURCES) 

clean:
    -rm -rf $(BIN) bin/ obj/ core


Socket編程實踐(3) --Socket API