目录

Socket了解

Socket的流程和函数

Socket套接字

  • windows

    • socket
  • linux

    • file descriptor -> fd
  • 监听socket

    • 由服务器端创建
    • 绑定(bind)IP address 和 端口port
    • 调用了listen监听的
  • easy function list

    name description
    socket create a socket by type
    bind 将socket绑定到一个IP与端口的二元组上
    (通常是sockaddr类型数组)
    listen 让socket变为监听状态
    connect try to build a TCP connect(use in Client usaully)
    accept 尝试接受一个连接(use in Server)
    send 通过一个socket发送数据
    recv 通过一个socket接收数据
    select 判断一组socket上的读写事件/异常事件
    gethostbyname 通过域名获取机器地址
    close 关闭一个socket,并回收对应的资源
    windows : closesocket
    shutdown 关闭socket收发通道
    setsockopt set a option of socket
    getsockopt get a option of socket

TCP通信流程

  • 服务器Server
    1. 调用soket函数创建socket(listened)
    2. 调用bind将socket绑定到一个IP与端口的二元组上
    3. 调用listen开启监听
    4. 当有客户端请求连接,调用accept接收连接,产生一个new socket(Client)
    5. 通过新产生的client socket,调用send/recv与客户端交流数据
    6. 结束通信,调用close关闭监听soket(client’s socket also close)
  • 客户端Client
    1. 调用socket创建Client’s socket
    2. 调用connect尝试连接服务器
    3. 连接成功后调用send/recv与服务器交流数据
    4. 结束通信,调用close关闭socket

跨平台Socket

1
2
3
4
5
#ifdef WIN32
typedef SOCKET SOCKET_TYPE;
#else
typedef int SOCKET_TYPE;
#endif
  • socket调用失败返回-1(Windows定义)

  • linux没有这个宏,但可以定义一个

    1
    2
    3
    4
    5
    
    #define INVALID_HANDLE_VALUE (-1)
    
    #ifnedf WIN43
    #define SOCKET_ERROR (-1)
    #endif
    
  • 在linux上可以直接使用socket函数

  • 而windows上必须先调用 WSAStartup 函数将socket将dll文件加载到进程地址空间 中 程序退出时用 WSACLeanup卸载相关dll文件.

    • ⚠️dll文件是进程相关的,每个线程共享此资源,某个线程调用后,所有线程都可以继续使用.
  • 关闭socket函数

    • windows上不按标准

      1
      2
      3
      
      #ifdef WIN32
      #define closesocket(s) close(s)
      #endif
      
  • select函数在windows下第一个参数是不使用的,可随意设置

bind函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct sockaddr_in bindaddr; // socket 地址

bindaddr.sin_family = AF_INET; // 地址族
    /*
        常见的地址族有:
    - AF_INET: IPv4地址族,用于Internet协议第4版
    - AF_INET6: IPv6地址族,用于Internet协议第6版
    - AF_UNIX: UNIX域套接字,用于同一台机器上的进程间通信
    - AF_PACKET: 用于 Packet sockets 发送或接收数据包
    - AF_NETLINK: 用于内核用户空间通信的套接字    */

//bindaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
// 转换成对应大端小端->0.0.0.0:接收任意本地地址连接

bindaddr.sin_port = htons(10004);

if (bind(listenfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1)
{
    std::cout << "bind listen socket error." << std::endl;
	return -1;
}

sockaddr

  • 是一种通用的socket地址结构, 不同的协议地址长度不同.

  • sockaddr_in 是sockaddr的一个具体实现.用于IPv4,有IP地址和端口

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    /* Structure describing an Internet socket address.  */
    struct sockaddr_in
      {
        __SOCKADDR_COMMON (sin_); //sin_family
        in_port_t sin_port;			/* Port number.  */
        struct in_addr sin_addr;		/* Internet address.  */
    
        /* Pad to size of `struct sockaddr'.  */
        unsigned char sin_zero[sizeof (struct sockaddr)
    			   - __SOCKADDR_COMMON_SIZE
    			   - sizeof (in_port_t)
    			   - sizeof (struct in_addr)];
      };
    

sin_family 地址族(Address Family)

    - AF_INET: IPv4地址族,用于Internet协议第4版
        - AF_INET6: IPv6地址族,用于Internet协议第6版
        - AF_UNIX: UNIX域套接字,用于同一台机器上的进程间通信
        - AF_PACKET: 用于 Packet sockets 发送或接收数据包
        - AF_NETLINK: 用于内核用户空间通信的套接字

sin_addr.s_addr 指定IP地址

  • 用htonl将宏/int类型的IP地址数字转换为IP地址

    1
    2
    3
    4
    5
    6
    
    /* Address to accept any incoming messages.  */
    #define	INADDR_ANY		((in_addr_t) 0x00000000)
    /* Address to send to all hosts.  */
    #define	INADDR_BROADCAST	((in_addr_t) 0xffffffff)
    /* Address indicating an error return.  */
    #define	INADDR_NONE		((in_addr_t) 0xffffffff)
    
    • INADDR_ANY宏: 一串0的IP地址,表示 协议层会自动选一个合适的IP地址.
  • 用inet_addr将地址字符串转换为IP地址

    1
    
    bindaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    
  • IP地址的选择

    • INADDR_ANY 相当于地址0.0.0.0
      • 可以被公网访问,相当于绑定了外网IP
    • 局域网中的地址IP
      • 可以被局域网内部机器访问
    • 127.0.0.1 本地回环地址
      • 只能被本机访问

sin_port 绑定端口

htonl,htons|ntohl,ntohs

意思是:host byte order to network byte order (long/short)

​ network byte order to host byte order (long/short)

network byte order:big-endian 大端模式

host byte order: 根据机器决定.intel机器是little-endian 小端序列

1
2
3
4
5
6
7
8
9
#include <arpa/inet.h>

    uint32_t htonl(uint32_t hostlong);

    uint16_t htons(uint16_t hostshort);

    uint32_t ntohl(uint32_t netlong);

    uint16_t ntohs(uint16_t netshort);
  • 如果port设为0,操作系统会随机为程序分配一个可用的监听端口,Server一般不会绑定0 - port.因为Server要对外开放,必须让客户端知道确切的IP和Port.

⚠️sockaddr_in 的其他空间用sin_zero char数组填满以对齐sockaddr的空间

select函数