网络套接字socket

标签:char   end   成员   sizeof   流式   系统封装   etl   客户端   toc   

socket

??socket本质是插板和插座的意思,要完成数据的通信的套接字必须是成对出现的,即代表了插板和插座,,如下图所示,IP地址+端口号就对应了一个socket,一端的发送缓冲区对应了一端的接收缓冲区。建立连接的两个进程各自有一个socket来标识,那么这两个socket就组成的socket pair就唯一标识了一个连接,通过同一个文件描述符,进行对应的操作。
技术图片

网络字节序

??计算机通常采用的都是小端法存储,即高地址存储高位,地地址存地位,而网络字节序是采用大端法,即高位存在低地址,低位存在高地址。所以计算机将数据填到发送缓冲区之前应该将字节序转换成大端字节序(如果发送主机是大端字节序的就不需要进行转换),而接收端解析接收缓冲区的数据前应该将大端字节序的数据转换成小端字节序(如果接收主机是大端字节序的就不需要进行转换)。一些以下是字节序转换函数:

uint32_t htonl(uint32_t hostlong);  // 主机字节序到网络字节序 无符号长整型(IP)
uint16_t htons(uint16_t hostshort);// 主机字节序到网络字节序 无符号短整型 (port)
uint32_t ntohl(uint32_t netlong); // 网络字节序转为主机字节序 无符号长整型(IP)
uint16_t ntohs(uint16_t netshort); // 网络字节序转为主机字节序 无符号短整型(port)

IP地址转换函数

??IP地址是点分十进制的string,转换成网络字节序的过程如下:

192.168.100.102--->atoi--->int--->htonl--->网络字节序

上面的的转换过程很麻烦,所以系统封装如下函数:

inet_pton()
    函数功能:将主机的IP转换成网络字节序IP
    头 文 件:
            #include <arpa/inet.h>
    定义函数:
            int inet_pton(int af,const char *src, void *dst)
    参数分析:
            af:AF_INET(IPV4),AF_INET6(IPV6) 
            src:传入的IP地址
            dst:传出的网络字节序IP地址
    返 回 值:
            成功:1
            异常:0,表明src不是一个有效的IP地址
            失败:-1

inet_ntop()
    函数功能:网络字节序IP转换成主机IP
    头 文 件:
            #include <arpa/inet.h>
    定义函数:
            const char* inet_ntop(int af,const char *src, void *dst, socklen_t size)
    参数分析:
            af:AF_INET(IPV4),AF_INET6(IPV6) 
            src:传入的IP地址
            dst:传出的网络字节序IP地址
    返 回 值:
            成功:dst
            失败:NULL

以上函数可转换IPV4和IPV6类型的地址,以下函数只可以转换IPV4类型的地址

头文件:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// 把cp指向的字符串转换为32位的网络字节序的二进制值存于inp中
int inet_aton(const char *cp, struct in_addr *inp);
inet_aton("192.168.100.102", &inp);
        
// 把cp指向的字符串转换为32位的网络字节序的二进制值并返回
in_addr_t inet_addr(const char *cp);
in_addr_t inet_network(const char *cp);
        
// 把in中的32位网络字节序的二进制地址转换为点分十进制的字符串
char *inet_ntoa(struct in_addr in);

TCP服务器,客户端模型创建流程图

技术图片
??注意:上图中创建了三个套接字

相关API

socket()
    函数功能:
            创建套接字
    头 文 件:
            #include <sys/types.h>       
            #include <sys/socket.h>
    定义函数:
            int socket (int domain, int type, int protocol)
    参数分析:
            domain:
                  AF_INET/PF_INET: 网际协议
                  AF_UNIX/PF_UNIX:本地协议,可写成 AF_LOCAL/PF_LOCA
            type:
                  SOCK_STREAM:流式套接字,TCP
                  SOCK_DGRAM:数据报套接字,UDP
            protocol:协议, 一般为 0
    返 回 值:
            成功:待连接套接字
            失败:-1

bind()
    函数功能:
            将套接字绑定上IP地址和端口号
    头 文 件:
            #include <sys/types.h>          
            #include <sys/socket.h>
    定义函数:
            int bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)   
    参数分析:
            sockfd: 新创建的套接字
              addr: 包含了IP地址和端口的结构体指针,该结构体用于设置它们
            addrlen:结构体addr的大小       
    返 回 值:
            成功: 0
            失败:-1
    备    注:
            参数2被结构体sockaddr_in代替了,因为结构体sockaddr 没有将IP地址和端口号分别用结构体成员保存,设置和操作的时候不太方便。

listen()
    函数功能:
            设置允许同时建立连接的上线数
    头 文 件:
            #include <sys/types.h>          /* See NOTES */
            #include <sys/socket.h>
    定义函数:
            int listen (int sockfd, int backlog)
    参数分析:
             sockfd:待连接套接字
            backlog:允许最大同时接收连接请求个数
    返 回 值:
            成功:0,并将sockfd设置为监听套接字
            失败:-1

accept()
    函数功能:
            等待对端连接请求
    头 文 件:
            #include <sys/types.h>         
            #include <sys/socket.h>
    定义函数:
            int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen)
    参数分析:
            sockfd:监听套接字
              addr:传入参数,用以存储对端地址(IP+PORT)
           addrlen:参数 addr 的存储区域大小
    返 回 值:
            成功:返回一个新的套接字,旧的套接字将用于监听是否有新的连接请求
            失败:-1

connect()
    函数功能:
            连接对端监听套接字
    头 文 件:
            #include <sys/types.h>         
            #include <sys/socket.h>
    定义函数:
            int connect (int sockfd, const struct sockaddr *addr, socklen_taddrlen);
    参数分析:
            sockfd:待连接套接字
              addr:传入参数,用以存储对端地址(IP+PORT)
           addrlen:地址结构体大小
    返 回 值:
            成功:0
            失败:-1

send()
    函数功能:
            向TCP套接字发送数据
    头 文 件:
            #include <sys/types.h>
            #include <sys/socket.h>
    定义函数:
            ssize_t send (int sockfd, const void *buf, size_t len, int flags)
    参数分析:
            sockfd:已连接套接字
               buf:即将被发送的数据
               len:数据长度
             flags:发送标志
                  MSG_NOSIGNAL:当对端已关闭时,不产生 SIGPIPE 信号
                  MSG_OOB:发送紧急(带外)数据,只针对 TCP 连接
    返 回 值:
            成功:已发送字节数
            失败:-1

recv()
    函数功能:
            从TCP套接字中接收数据
    头 文 件:
            #include <sys/types.h>
            #include <sys/socket.h>
    定义函数:
             ssize_t recv (int sockfd, void *buf, size_t len, int flags);
    参数分析:
            sockfd:已连接套接字
               buf:存储数据缓冲区
               len:缓冲区大小
             flags:接收标志
                  MSG_OOB:接收紧急(带外)数据
    返 回 值:
            成功:已接收字节数
            失败:-1
              

TCP实验程序

server.c

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

int main(int argc, char const *argv[])
{
    int ret_val;
    char *rev_data = (char *)calloc(100, sizeof(char));
    if (rev_data == NULL)
    {
        printf("calloc error");
    }
    
   // printf("bind\n");
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sock_fd)
    {
        perror("socket error");
        return -1;
    }
     
    struct sockaddr_in server_addr;
    bzero(&server_addr , sizeof(server_addr)); 
    server_addr.sin_family = AF_INET; //IPV4
    server_addr.sin_port = htons(65110);
    server_addr.sin_addr.s_addr = inet_addr("192.168.200.200");//将服务器地址设置成本地网卡的其中一个IP地址

    ret_val = bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (-1 == ret_val)
    {
        perror("bind error");
        return -1;
    }
    printf("bind successfully\n");


    ret_val = listen(sock_fd, 2);
    if (-1 == ret_val)
    {
        perror("listen error");
        return -1;
    }
    
    printf("listen successfully\n");

    struct sockaddr_in other_addr;
    socklen_t addrlen = sizeof(other_addr);
    int connect_fd = accept(sock_fd, (struct sockaddr *)&other_addr, &addrlen);
    if (-1 == connect_fd)
    {
        perror("accept error");
        return -1;
    }
    printf("accept successfully\n");
    
    while (1)
    {
        bzero(rev_data , 100);
        ret_val = recv(connect_fd, rev_data, 100, 0);
        if (ret_val > 0)
        {
            printf("recv msg: %s", rev_data); 
        }
    }
    
    close(connect_fd);
    close(sock_fd);
    return 0;
}

client.c

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


int main(int argc, char const *argv[])
{
  
    int sock_fd = socket( AF_INET, SOCK_STREAM, 0);
    if (-1 == sock_fd)
    {
        perror("socket error");
        return -1 ;
    }
    printf("socket successfully\n");
    
    struct sockaddr_in client_addr ;  // 定义一个结构体变量
    bzero(&client_addr , sizeof(client_addr)); // 清空结构体
    client_addr.sin_family = AF_INET ; // IPV4地址协议
    client_addr.sin_port = htons(65110) ;
    client_addr.sin_addr.s_addr = inet_addr("192.168.200.200"); // 应该设置为服务器的IP地址

    int ret_val= connect(sock_fd, (struct sockaddr *)&client_addr,sizeof(client_addr));
    if (-1 == ret_val )
    {
        perror("connect error");
        return -1 ;
    }
    printf("connect successfully\n");
    
    char buf [100] ;
	struct in_addr addr;
	addr.s_addr = client_addr.sin_addr.s_addr;

    while(1)
    {
        fgets(buf , 100 , stdin);
        ret_val = send(sock_fd , buf , strlen(buf) , 0 );
        if (ret_val > 0)
        {
            printf("server ip:%s server port:%d  send data is:%s", 
                    inet_ntoa(addr), ntohs(client_addr.sin_port), buf);
        }
    }

    close(sock_fd);

    return 0;
}

输出结果:
技术图片

总结

??1.发起TCP连接请求被拒绝是由于目标服务器上无对应的监听套接字,需要检查IP地址和端口号是否对应;
??2.服务器的地址应该与本地网卡的其中一个IP地址对应;

网络套接字socket

标签:char   end   成员   sizeof   流式   系统封装   etl   客户端   toc   

原文地址:https://www.cnblogs.com/ding-ding-light/p/14290725.html

版权声明:完美者 发表于 2021-01-19 11:42:22。
转载请注明:网络套接字socket | 完美导航

暂无评论

暂无评论...