socket简析

socket 是“套接字”的意思,是计算机之间进行通信的一种约定,也可以认为是一种技术。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

socket里面最底层为:

typedef __uint32_t  in_addr_t;  /* base type for internet address */

接着它被放到了一个叫做in_addr的结构体里面了,作为后面的使用。

/*
 * Internet address (a structure for historical reasons)
 */
struct in_addr {
    in_addr_t s_addr;
};

再接着它被放到了sockaddr_in这个结构体里面了,即socket地址,它为internet方式。

/*
 * Socket address, internet style.
 */
struct sockaddr_in {
    __uint8_t   sin_len;
    sa_family_t sin_family;
    in_port_t   sin_port;
    struct  in_addr sin_addr;
    char        sin_zero[8];
};

在socket.h文件里面再次定义了socket地址,里面包含它的长度,地址族,和地址值。

struct sockaddr {
    __uint8_t   sa_len;     /* total length */
    sa_family_t sa_family;  /* [XSI] address family */
    char        sa_data[14];    /* [XSI] addr value (actually larger) */
};

那么接下来看看地址族(address family)吧。

/*
 * Address families.
 */
#define AF_UNSPEC   0       /* unspecified */
#define AF_UNIX     1       /* local to host (pipes) */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define AF_LOCAL    AF_UNIX     /* backward compatibility */
#endif  /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#define AF_INET     2       /* internetwork: UDP, TCP, etc. */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define AF_IMPLINK  3       /* arpanet imp addresses */
#define AF_PUP      4       /* pup protocols: e.g. BSP */
#define AF_CHAOS    5       /* mit CHAOS protocols */
#define AF_NS       6       /* XEROX NS protocols */
#define AF_ISO      7       /* ISO protocols */
#define AF_OSI      AF_ISO
#define AF_ECMA     8       /* European computer manufacturers */
#define AF_DATAKIT  9       /* datakit protocols */
#define AF_CCITT    10      /* CCITT protocols, X.25 etc */
#define AF_SNA      11      /* IBM SNA */
#define AF_DECnet   12      /* DECnet */
#define AF_DLI      13      /* DEC Direct data link interface */
#define AF_LAT      14      /* LAT */
#define AF_HYLINK   15      /* NSC Hyperchannel */
#define AF_APPLETALK    16      /* Apple Talk */
#define AF_ROUTE    17      /* Internal Routing Protocol */
#define AF_LINK     18      /* Link layer interface */
#define pseudo_AF_XTP   19      /* eXpress Transfer Protocol (no AF) */
#define AF_COIP     20      /* connection-oriented IP, aka ST II */
#define AF_CNT      21      /* Computer Network Technology */
#define pseudo_AF_RTIP  22      /* Help Identify RTIP packets */
#define AF_IPX      23      /* Novell Internet Protocol */
#define AF_SIP      24      /* Simple Internet Protocol */
#define pseudo_AF_PIP   25      /* Help Identify PIP packets */
/*define pseudo_AF_BLUE 26     Identify packets for Blue Box - Not used */
#define AF_NDRV     27      /* Network Driver 'raw' access */
#define AF_ISDN     28      /* Integrated Services Digital Network*/
#define AF_E164     AF_ISDN     /* CCITT E.164 recommendation */
#define pseudo_AF_KEY   29      /* Internal key-management function */
#endif  /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#define AF_INET6    30      /* IPv6 */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define AF_NATM     31      /* native ATM access */
#define AF_SYSTEM   32      /* Kernel event messages */
#define AF_NETBIOS  33      /* NetBIOS */
#define AF_PPP      34      /* PPP communication protocol */
#define pseudo_AF_HDRCMPLT 35       /* Used by BPF to not rewrite headers
                     * in interface output routine */
#define AF_RESERVED_36  36      /* Reserved for internal usage */
#define AF_IEEE80211    37              /* IEEE 802.11 protocol */
#define AF_UTUN     38
#define AF_MAX      40
#endif  /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */

这里面包括了常用的AF_INET,其中包括TCP,UDP等等其它的地址族。

而对于协议族,其目前和地址族是一样的。

/*
 * Protocol families, same as address families for now.
 */
#define PF_UNSPEC   AF_UNSPEC
#define PF_LOCAL    AF_LOCAL
#define PF_UNIX     PF_LOCAL    /* backward compatibility */
#define PF_INET     AF_INET
#define PF_IMPLINK  AF_IMPLINK
#define PF_PUP      AF_PUP
#define PF_CHAOS    AF_CHAOS
#define PF_NS       AF_NS
#define PF_ISO      AF_ISO
#define PF_OSI      AF_ISO
#define PF_ECMA     AF_ECMA
#define PF_DATAKIT  AF_DATAKIT
#define PF_CCITT    AF_CCITT
#define PF_SNA      AF_SNA
#define PF_DECnet   AF_DECnet
#define PF_DLI      AF_DLI
#define PF_LAT      AF_LAT
#define PF_HYLINK   AF_HYLINK
#define PF_APPLETALK    AF_APPLETALK
#define PF_ROUTE    AF_ROUTE
#define PF_LINK     AF_LINK
#define PF_XTP      pseudo_AF_XTP   /* really just proto family, no AF */
#define PF_COIP     AF_COIP
#define PF_CNT      AF_CNT
#define PF_SIP      AF_SIP
#define PF_IPX      AF_IPX      /* same format as AF_NS */
#define PF_RTIP     pseudo_AF_RTIP  /* same format as AF_INET */
#define PF_PIP      pseudo_AF_PIP
#define PF_NDRV     AF_NDRV
#define PF_ISDN     AF_ISDN
#define PF_KEY      pseudo_AF_KEY
#define PF_INET6    AF_INET6
#define PF_NATM     AF_NATM
#define PF_SYSTEM   AF_SYSTEM
#define PF_NETBIOS  AF_NETBIOS
#define PF_PPP      AF_PPP
#define PF_RESERVED_36  AF_RESERVED_36
#define PF_UTUN     AF_UTUN
#define PF_MAX      AF_MAX

下面是Socket的类型,最常见的为sock_stream和sock_dgram,分别为TCP和UDP的数据传输方式

/*
 * Types
 */
#define SOCK_STREAM 1       /* stream socket */
#define SOCK_DGRAM  2       /* datagram socket */
#define SOCK_RAW    3       /* raw-protocol interface */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define SOCK_RDM    4       /* reliably-delivered message */
#endif  /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
#define SOCK_SEQPACKET  5       /* sequenced packet stream */
  • SOCK_STREAM
    表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。

  • SOCK_DGRAM
    表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。


对于TCP和UDP的区别如下
TCP是面向连接的,交互双方的进程各自建立一个流式套接字,服务器需要等待客户端向其提出连接申请。一旦接受客户端申请就立刻返回一个新的套接字描述符。通过该描述符调用数据传输函数与客户端进行数据的收发。

UDP是面向无连接的,双方建立的是数据报套接字,服务器和客户端在进行传描数据之前不需要进行连接的申请和建立,可以随时向对方发消息。

协议 TCP UDP
数据传输方式 流式 数据报
优点 可靠、稳定 速度快,比TCP稍安全
缺点 速度慢、效率低、占用系统资源高、易被攻击 不可靠、不稳定
适合场景 网络通讯质量要求高(可靠、稳定) 网络通讯质量要求不高,速度快。

需要区分协议与服务、端口的区别:
服务一般和端口对应,比如Web服务对应80端口,而FTP服务对应21端口,SMTP服务对应25端口
而协议指的是类似于TCP等协议。服务中用到协议。比如FTP中用到FTP协议。

这是服务器端的代码:

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <zconf.h>

int UDP()
{
    int server_sockfd;
    ssize_t len;
    struct sockaddr_in my_addr;   //服务器网络地址结构体
    struct sockaddr_in remote_addr; //客户端网络地址结构体
    socklen_t sin_size;
    char buf[BUFSIZ];  //数据传送的缓冲区
    memset(&my_addr, 0, sizeof(my_addr)); //数据初始化--清零
    my_addr.sin_family = AF_INET; //设置为IP通信
    my_addr.sin_addr.s_addr = INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
    my_addr.sin_port = htons(8000); //服务器端口号

    /*创建服务器端套接字--IPv4协议,面向无连接通信,UDP协议*/
    if((server_sockfd = socket(PF_INET, SOCK_DGRAM, 0))<0)
    {
        perror("socket error");
        return 1;
    }

    /*将套接字绑定到服务器的网络地址上*/
    if (bind(server_sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) < 0)
    {
        perror("bind error");
        return 1;
    }

    printf("my address %s\n", inet_ntoa(my_addr.sin_addr));
    sin_size = sizeof(struct sockaddr_in);
    printf("waiting for a packet...\n");

    /*接收客户端的数据并将其发送给客户端--recvfrom是无连接的*/
    if((len = recvfrom(server_sockfd, buf, BUFSIZ, 0, (struct sockaddr *)&remote_addr, &sin_size)) < 0)
    {
        perror("recvfrom error");
        return 1;
    }
    printf("received packet from %s:\n", inet_ntoa(remote_addr.sin_addr));
    buf[len] = '\0';
    printf("contents: %s\n", buf);

    /*关闭套接字*/
    close(server_sockfd);

    return 0;
}

int TCP()
{
    int server_sockfd;//服务器端套接字
    int client_sockfd;//客户端套接字
    ssize_t len;
    struct sockaddr_in my_addr;   //服务器网络地址结构体
    struct sockaddr_in remote_addr; //客户端网络地址结构体
    socklen_t sin_size;
    char buf[BUFSIZ];  //数据传送的缓冲区
    memset(&my_addr, 0, sizeof(my_addr)); //数据初始化--清零
    my_addr.sin_family = AF_INET; //设置为IP通信
    my_addr.sin_addr.s_addr = INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
    my_addr.sin_port = htons(8000); //服务器端口号

    /*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/
    if((server_sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket error");
        return 1;
    }

    /*将套接字绑定到服务器的网络地址上*/
    if(bind(server_sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) < 0)
    {
        perror("bind error");
        return 1;
    }

    printf("my address %s\n", inet_ntoa(my_addr.sin_addr));

    /*监听连接请求--监听队列长度为5*/
    if(listen(server_sockfd, 5) < 0)
    {
        perror("listen error");
        return 1;
    };

    sin_size = sizeof(struct sockaddr_in);

    /*等待客户端连接请求到达*/
    if((client_sockfd = accept(server_sockfd, (struct sockaddr *)&remote_addr, &sin_size))<0)
    {
        perror("accept error");
        return 1;
    }
    printf("accept client %s\n", inet_ntoa(remote_addr.sin_addr));
    len = send(client_sockfd, "Welcome to my server\n", 21, 0);//发送欢迎信息

    /*接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,send返回发送的字节数*/
    while((len = recv(client_sockfd, buf, BUFSIZ, 0)) > 0)
    {
        buf[len] = '\0';
        printf("%s\n", buf);
        if(send(client_sockfd, buf, len, 0) < 0)
        {
            perror("write error");
            return 1;
        }
    }

    /*关闭套接字*/
    close(client_sockfd);
    close(server_sockfd);

    return 0;
}

int main(int argc, char *argv[])
{
    UDP();
    return 0;
}

这是客户端的代码:

#include <cstdio>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <zconf.h>

int UDP()
{
    int client_sockfd;
    ssize_t len;
    struct sockaddr_in remote_addr; //服务器端网络地址结构体
    socklen_t sin_size;
    char buf[BUFSIZ];  //数据传送的缓冲区
    memset(&remote_addr, 0, sizeof(remote_addr)); //数据初始化--清零
    remote_addr.sin_family = AF_INET; //设置为IP通信
    remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器IP地址
    remote_addr.sin_port = htons(8000); //服务器端口号

    /*创建客户端套接字--IPv4协议,面向无连接通信,UDP协议*/
    if((client_sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
    {
        perror("socket error");
        return 1;
    }
    strcpy(buf,"This is a test message!"); // 发送的内容
    printf("sending: '%s'\n",buf);
    sin_size = sizeof(struct sockaddr_in);

    /*向服务器发送数据包*/
    if((len = sendto(client_sockfd, buf, strlen(buf), 0, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr))) < 0)
    {
        perror("recvfrom");
        return 1;
    }

    /*关闭套接字*/
    close(client_sockfd);

    return 0;
}

int TCP()
{
    int client_sockfd;
    ssize_t len;
    struct sockaddr_in remote_addr; //服务器端网络地址结构体
    char buf[BUFSIZ];  //数据传送的缓冲区
    memset(&remote_addr, 0, sizeof(remote_addr)); //数据初始化--清零
    remote_addr.sin_family = AF_INET; //设置为IP通信
    remote_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器IP地址
    remote_addr.sin_port = htons(8000); //服务器端口号

    /*创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/
    if((client_sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket error");
        return 1;
    }

    /*将套接字绑定到服务器的网络地址上*/
    if(connect(client_sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) < 0)
    {
        perror("Connect error");
        return 1;
    }
    printf("connected to server\n");
    len = recv(client_sockfd, buf, BUFSIZ, 0);//接收服务器端信息
    buf[len] = '\0';
    printf("%s", buf); //打印服务器端信息

    /*循环的发送接收信息并打印接收信息(可以按需发送)--recv返回接收到的字节数,send返回发送的字节数*/
    while(1)
    {
        printf("Enter string to send:");
        scanf("%s", buf);
        if(!strcmp(buf, "quit"))
            break;
        len = send(client_sockfd, buf, strlen(buf), 0);
        len = recv(client_sockfd, buf, BUFSIZ, 0);
        buf[len] = '\0';
        printf("received:%s\n", buf);
    }

    /*关闭套接字*/
    close(client_sockfd);

    return 0;
}

int main(int argc, char *argv[]){
    UDP();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qxconverse/article/details/79496382