一 TCP与UDP的异同

       TCP与UDP均工作于传输层,TCP是有连接的,安全的,注重数据完整性;UDP是无连接的,不安全的,注重实时性。这里的有连接和无连接的区别并不在于是否连接网线,而是在于在传输数据时是否需要建立连接。这里的安全并不是指的会不会被攻击或者其他,而是指数据完整性。TCP注重数据完整性,当对端没有接收到数据时,本端会再次发送。而UDP则注重于数据的实时性,发生丢包时并不会重发。这些特点也导致了两者的使用途径的不同,TCP用于数据比较重要,但是不注重时效性的数据,而UDP则用于数据丢包后影响较小的地方。

二  TCP,UDP通信流程

        此为Linux系统下的通信流程,从上图可以看出TCP在通信时比UDP多了listen(),accept(),connect()函数,这就是第一节所对比的有连接和无连接,UDP在创建套接字后直接就可以发送数据,而TCP则需要先建立连接。TCP的三次握手也是发生在这里。注意:两者的发送和接收函数也并不同,并且使用套接字后必须关闭。

        Windows与上图基本类似,但是多了几个函数,首先就是在使用套接字之前需要使用WSAStartup()初始化WinSock库,并且需要链接ws2_32.lib,使用完后不仅需要关闭套接字还需要清理环境WSACleanup()。

三 socket套接字常见API函数及使用方法

通用函数(适用于Windows和Linux)

  • socket(): 创建套接字。
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);

    sockfd为套接字描述符,参数一int domain:AF_INET为协议族,可选AF_INET(IPv4),AF_INET6(IPv6);  参数二int type:SOCK_STREAM为TCP协议类型,可选SOCK_DGRAM为UDP协议类型;   参数三int protocol:默认0; 错误返回-1;                                       

  • htons(): 绑定端口,将主机字节序转换为网络字节序(用于端口号)。                                                                          
  • struct sockaddr_in addr;
    addr.sin_family = AF_INET;         // IPv4 地址族
    addr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有可用IP
    addr.sin_port = htons(8080);       // 将端口号 8080 转换为网络字节序

    将端口8080转换为网络字节序,注:struct sockaddr_in  addr 为IP地址信息结构体。                           

     struct sockaddr_in addr;
        // 初始化结构体(所有成员设为 0)
        memset(&addr, 0, sizeof(addr));
    
        // 设置地址族为 IPv4
        addr.sin_family = AF_INET;
    
        // 设置端口号(8080,转换为网络字节序)
        addr.sin_port = htons(8080);
    
        // 关键:使用 inet_pton 转换 IP 地址到结构体的 sin_addr 成员
        // 将 "127.0.0.1" 转换为网络字节序,并存储到 addr.sin_addr 中
        if (inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr) <= 0) {
            std::cerr << "IP 地址转换失败" << std::endl;
  • ntohs(): 解析端口将网络字节序转换为主机字节序(用于获取对方端口号)。                                        
    uint16_t net_port = htons(8080);  // 模拟网络传输的端口号
        
    // 转换为本地主机字节序
    uint16_t host_port = ntohs(net_port);
  • inet_pton(): 配置目标IP,本地IP将IP地址从文本格式转换为二进制格式(网络字节序)。               
    inet_pton(AF_INET, "127.0.0.1",&addr.sin_addr)

    AF_INET:IPv4协议族;“127.0.0.1”:具体IP地址;&addr.sin_addr:需要存放IP地址的指针。成功返回 1,无效地址返回 0,错误返回 -1。

  • inet_ntop(): 打印对方IP,将IP地址从二进制格式(网络字节序)转换为文本格式。                               
    inet_ntop(AF_INET, &addr.sin_addr, ip_str, INET_ADDRSTRLEN) 

    &addr.sin_addr:存放二进制IP 的指针;ip_str:将要存放字符串地址的指针;INET_ADDRSTRLEN :字符穿IP指针指向的缓冲区大小;     

  • bind(): 服务器绑定本地IP和端口,以方便监听连接请求。返回值为0,-1(失败)
bind(sockfd, (struct sockaddr*)&addr, sizeof(addr))

sockfd:套节字描述符;(struct sockaddr*)&addr:本地IP地址结构体指针(转换为struct socket*类型)sizeof(addr):IP地址结构体的大小;

  • connect(): 客户端用于发起连接到服务器。
connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) 

sockfd:套接字 描述符; (struct sockaddr*)&serv_addr:服务器地址结构体;sizeof(serv_addr):服务器地址结构体的大小;   

  • listen(): 用于监听bind()绑定的IP和端口
listen(sockfd, backlog)

sockfd:已绑定地址的套接字描述符(通过 socket() 创建并经 bind() 绑定)backlog:监听队列的最大长度,用于限制等待接受的连接请求数量          

  • accept(): 接收客户端连接请求,并创建新的套接字用于数据传输。
int client_sock = accept(listen_sock, (struct sockaddr*)&client_addr, &client_addr_len);

sockfd:处于监听状态的套接字描述符;(struct sockaddr*)&client_addr:存储客户端的IP地址信息结构体;&client_len:该信息结构体的大小;

  • recv(): TCP接收数据。
recv(sockfd, buffer, sizeof(buffer) - 1, 0)

sockfd:本地已连接的套接字描述符(客户端的 sockfd 或服务器通过 accept() 获得的 client_sock);buffer:接收缓冲区指针;sizeof(buffer)-1:缓冲区的最大长度;0:默认0;             

  • send(): TCP发送数据。
// 成功返回发送的字节数,失败返回 -1(Windows 为 SOCKET_ERROR)
send(sockfd, msg, msg_len, 0)

 sockfd:本地已连接的套接字描述符(客户端的 sockfd 或服务器通过 accept() 获得的 client_sock);msg:待发送缓冲区指针;msg_len:缓冲区的最大长度;0:默认0;                       

  • recvfrom(): UDP接收数据。
recvfrom(sockfd, buffer, sizeof(buffer)-1, 0,(struct sockaddr*)&sender_addr, &sender_len);

 sockfd:UDP套接字;buffer:接收数据缓冲区;sizeof(buffer) -1:缓冲区最大长度;0:默认0;      (struct sockaddr*)&sender_addr输出参数,用于存储发送方的地址信息(struct sockaddr* 类型) ;&sender_len:输入时为 src_addr 大小,输出时为实际地址长度。         

  • sendto(): UDP发送数据。
sendto (sockfd, send_msg, strlen (send_msg), 0, (struct sockaddr*)&server_addr, sizeof (server_addr) 

 sockfd:UDP套接字;send_msg:待发送数据缓冲区;strlen(send_msg):缓冲区最大长度;0:默认0;(struct sockaddr*)&sender_addr目标地址(struct sockaddr* 类型) ;&sender_len:输入时为 src_addr 大小,输出时为实际地址长度。 

Windows特定函数

  • WSAStartup(): 使用socket套接字前,需初始化WinSock库。
  • WSACleanup(): 使用socket套接字后,需终止对WinSock库的使用。
  • closesocket(): 关闭套接字。
  • WSAGetLastError(): 获取错误码。

Linux特定函数

  • close(): 关闭套接字。
  • errno: 获取错误码(全局变量)。
WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);

 WSACleanup();
 closesocket(sockfd);
 close(sockfd)
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐