IPv4地址的結構體與網絡字節序


服務端套接字創建過程

第一步:調用socket函數創建套接字

//成功時返回文件表述符,失敗時返回-1
int socket(int __domain, int __type, int __protocol)
  • domain:套接字使用的協議族(Protocol Family)信息;
  • type:套接字數據傳輸類型信息;
  • protocol:計算機間通信中使用的協議信息;

協議族(Protocol Family)

 Name         Purpose                                    Man page
 AF_UNIX      Local communication                        unix(7)
 AF_LOCAL     Synonym for AF_UNIX
 AF_INET      IPv4 Internet protocols                    ip(7)
 AF_IPX       IPX - Novell protocols
 AF_INET6     IPv6 Internet protocols                    ipv6(7)
 AF_PACKET    Low-level packet interface                 packet(7)

套接字類型(Type):是指套接字的傳輸方式

  • 面向連接的套接字(SOCK_STREAM)
    • 傳輸過程中數據不會消失
    • 按序傳輸數據
    • 傳輸的數據不存在數據邊界(Boundary):調用了三次write傳遞了100字節,接受者僅一次read接收了全部
  • 面向消息的套接字(SOCK_DGRAM)
    • 強調快速傳輸而非傳輸順序
    • 傳輸的數據可能丟失也可能損毀
    • 傳輸的數據有數據邊界
    • 限制每次傳輸的數據大小

協議的最終選擇

/* Standard well-defined IP protocols.  */
enum
  {
    IPPROTO_IP = 0,	   /* Dummy protocol for TCP.  */
#define IPPROTO_IP		IPPROTO_IP
    IPPROTO_ICMP = 1,	   /* Internet Control Message Protocol.  */
#define IPPROTO_ICMP		IPPROTO_ICMP
    IPPROTO_IGMP = 2,	   /* Internet Group Management Protocol. */
#define IPPROTO_IGMP		IPPROTO_IGMP
    IPPROTO_IPIP = 4,	   /* IPIP tunnels (older KA9Q tunnels use 94).  */
#define IPPROTO_IPIP		IPPROTO_IPIP
    IPPROTO_TCP = 6,	   /* Transmission Control Protocol.  */
#define IPPROTO_TCP		IPPROTO_TCP
    IPPROTO_EGP = 8,	   /* Exterior Gateway Protocol.  */
#define IPPROTO_EGP		IPPROTO_EGP
    IPPROTO_PUP = 12,	   /* PUP protocol.  */
#define IPPROTO_PUP		IPPROTO_PUP
    IPPROTO_UDP = 17,	   /* User Datagram Protocol.  */
#define IPPROTO_UDP		IPPROTO_UDP
    IPPROTO_IDP = 22,	   /* XNS IDP protocol.  */
#define IPPROTO_IDP		IPPROTO_IDP
    IPPROTO_TP = 29,	   /* SO Transport Protocol Class 4.  */
#define IPPROTO_TP		IPPROTO_TP
    IPPROTO_DCCP = 33,	   /* Datagram Congestion Control Protocol.  */
#define IPPROTO_DCCP		IPPROTO_DCCP
    IPPROTO_IPV6 = 41,     /* IPv6 header.  */
#define IPPROTO_IPV6		IPPROTO_IPV6
    IPPROTO_RSVP = 46,	   /* Reservation Protocol.  */
#define IPPROTO_RSVP		IPPROTO_RSVP
    IPPROTO_GRE = 47,	   /* General Routing Encapsulation.  */
#define IPPROTO_GRE		IPPROTO_GRE
    IPPROTO_ESP = 50,      /* encapsulating security payload.  */
#define IPPROTO_ESP		IPPROTO_ESP
    IPPROTO_AH = 51,       /* authentication header.  */
#define IPPROTO_AH		IPPROTO_AH
    IPPROTO_MTP = 92,	   /* Multicast Transport Protocol.  */
#define IPPROTO_MTP		IPPROTO_MTP
    IPPROTO_BEETPH = 94,   /* IP option pseudo header for BEET.  */
#define IPPROTO_BEETPH		IPPROTO_BEETPH
    IPPROTO_ENCAP = 98,	   /* Encapsulation Header.  */
#define IPPROTO_ENCAP		IPPROTO_ENCAP
    IPPROTO_PIM = 103,	   /* Protocol Independent Multicast.  */
#define IPPROTO_PIM		IPPROTO_PIM
    IPPROTO_COMP = 108,	   /* Compression Header Protocol.  */
#define IPPROTO_COMP		IPPROTO_COMP
    IPPROTO_SCTP = 132,	   /* Stream Control Transmission Protocol.  */
#define IPPROTO_SCTP		IPPROTO_SCTP
    IPPROTO_UDPLITE = 136, /* UDP-Lite protocol.  */
#define IPPROTO_UDPLITE		IPPROTO_UDPLITE
    IPPROTO_MPLS = 137,    /* MPLS in IP.  */
#define IPPROTO_MPLS		IPPROTO_MPLS
    IPPROTO_RAW = 255,	   /* Raw IP packets.  */
#define IPPROTO_RAW		IPPROTO_RAW
    IPPROTO_MAX
  };

第二步:調用bind函數分配IP地址和端口號

//成功時返回0,失敗時返回-1
int bind(int __fd, const struct sockaddr *__addr, socklen_t __len)
  • __fd:要分配地址信息(IP地址和端口號)的套接字文件表述符。
  • __addr:存有地址信息的結構體變量地址值
  • __len:第二個結構體變量的長度

第三步:調用listen函數轉為可接收請求狀態

//成功時返回0,失敗時返回-1
int listen(int __fd, int __n)
  • __fd:希望進入等待連接請求狀態的套接字文件描述符,傳遞的描述符套接字參數成為服務器端套接字(監聽套接字)。
  • __n:連接請求等待隊列的長度。

第四步:調用accept函數受理連接請求

//成功時返回0,失敗時返回-1
int accept(int __fd, struct sockaddr *__restrict__ __addr, socklen_t *__restrict__ __addr_len)
  • __fd:服務器套接字的文件描述符。
  • __addr:保存發起連接請求的客戶端地址信息地變量地址值,調用函數后向傳遞來的地址變量參數填充客戶端的地址信息。
  • __addr_len:第二個參數__addr結構體的長度,但是存有長度的變量地址。函數調用完后,該變量即被填入客戶端地址長度。

客戶端創建套接字過程

第一步:調用socket函數創建套接字

//成功時返回文件表述符,失敗時返回-1
int socket(int __domain, int __type, int __protocol)

第二步:調用socket函數向服務器端發送連接請求

//成功時返回0,失敗時返回-1
int connect(int __fd, const struct sockaddr *__addr, socklen_t __len)

編寫

echosrv.c

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

int main(int argc, char** argv) {
    // 1. 創建套接字
    int listenfd;
                          //協議族    套接字類型    協議類型
    if ((listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
       perror("socket");
    }

    // 2. 分配套接字地址
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof servaddr);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(6666);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    // servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    // inet_aton("127.0.0.1", &servaddr.sin_addr);

    int on = 1;
    // 確保time_wait狀態下同一端口仍可使用
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
    {
        perror("setsockopt");
    }

    // 3. 綁定套接字地址
    if (bind(listenfd, (struct sockaddr*) &servaddr, sizeof servaddr) < 0) {
        perror("bind");
    }
    // 4. 等待連接請求狀態
    if (listen(listenfd, SOMAXCONN) < 0) {
        perror("listen");
    }
    // 5. 允許連接
    struct sockaddr_in peeraddr;
    socklen_t peerlen = sizeof peeraddr;
    int connfd;
    if ((connfd = accept(listenfd, (struct sockaddr *) &peeraddr, &peerlen)) < 0) {
        perror("accept");
    }

    printf("id = %s, ", inet_ntoa(peeraddr.sin_addr));
    printf("port = %d\n", ntohs(peeraddr.sin_port));

    // 6. 數據交換
    char recvbuf[1024];
    while (1)
    {
        memset(recvbuf, 0, sizeof recvbuf);
        int ret = read(connfd, recvbuf, sizeof recvbuf);
        if (ret == 0)
        {
            printf("client close\n");
            break;
        } else if (ret == -1)
        {
            perror("read");
        }
        fputs(recvbuf, stdout);
        write(connfd, recvbuf, ret);
    }

    // 7. 斷開連接
    close(connfd);
    close(listenfd);
    return 0;
}

echocli.c

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

int main()
{
    // 1. 創建套接字
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        perror("socket");
    }

    // 2. 分配套接字地址
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof servaddr);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(6666);
    // servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
     servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    // inet_aton("127.0.0.1", &servaddr.sin_addr);

    // 3. 請求鏈接
    if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof servaddr) < 0) {
        perror("connect");
    }

    // 4. 數據交換
    char recvbuf[1024] = {0};
    char sendbuf[1024] = {0};
    while (fgets(sendbuf, sizeof sendbuf, stdin) != NULL)   // 鍵盤輸入獲取
    {
//        memset(recvbuf, 0, sizeof recvbuf);
//        memset(sendbuf, 0, sizeof sendbuf);
        write(sockfd, sendbuf, sizeof sendbuf); // 寫入服務器
        int ret = read(sockfd, recvbuf, sizeof recvbuf);    // 服務器讀取
        if (ret == 0)
        {
            printf("server close\n");
            break;
        } else if (ret == -1)
        {
            perror("read");
        }
        fputs(recvbuf, stdout); // 服務器返回數據輸出

        // 清空
        memset(recvbuf, 0, sizeof recvbuf);
        memset(sendbuf, 0, sizeof sendbuf);
    }

    // 5. 斷開連接
    close(sockfd);

    return 0;
}


免責聲明!

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



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