美文网首页
第五章|Linux网络编程基础API

第五章|Linux网络编程基础API

作者: dongshixiao | 来源:发表于2018-03-28 16:44 被阅读0次

主要函数

  • 结构体 sockaddr
    表示通用socket地址
    *结构体 sockaddr_in(in_addr) 、sockaddr_in6(in6_addr)
    ipv4、ipv6专用地址结构体

  • 函数 inet_pton
    讲字符串表示ip地址转换成网络字节序整数表示的ip地址。

  • 函数 socket
    调用系统创建socket

  • 函数 bind
    将一个socket与socket地址绑定。也称命名

  • 函数listen
    socket命名后需要创建一个监听队列以存放待处理的客户连接。

  • 函数accept
    调用listen监听队列中接受一个连接。

  • 函数connect
    客户端通过系统调用主动与服务器建立连接

  • 函数close
    关闭对应的socket

  • 函数recv、send
    tcp数据读写
    读取、写入sockfd上的数据

  • 函数recvfrom、sendto
    udp数据读写
    读取、写入sockfd上的数据

  • 函数recvmsg、sendmsg
    通用数据读写

  • 函数getsockname、getpeername
    获取sockfd对应的本端socket地址
    获取sockfd对应的远端socket地址

  • 函数getsockopt、setsockopt
    专门读取和设置socket文件描述符属性的方法

主机字节序和网络字节序

理解字节序可以参考阮一峰老师的文档:
http://www.ruanyifeng.com/blog/2016/11/byte-order.html

举例来说,数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11

  • 大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。
  • 小端字节序:低位字节在前,高位字节在后,即以0x1122形式储存。
0x1234567的大小端字节序

检查机器的字节序

#include <stdio.h>

void byteorder()
{
    union
    {
        short value;
        char union_bytes[sizeof(short)];
    } test;
    test.value = 0x0102;
    if ((test.union_bytes[0] == 1) && test.union_bytes[1] == 2)
    {
        printf("big endian\n");
    }
    else if ( (test.union_bytes[0] == 2) && (test.union_bytes[1] == 1))
    {
        printf("little endian\n");
    }
    else
    {
        printf('unknown..\n');
    }
}

int main() {
    //printf("Hello, World!\n");
    byteorder();
    return 0;
}

现在pc大多采用小端字节序,因此小端字节序又被称为主机字节序。
大端也称为网络字节序。
即使是同一台机器上的两个进程通信,也要考虑字节序的问题。

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

static bool stop = false;
//SIGTERM信号处理函数,触发时结束主程序中的循环
static void handle_term( int sig )
{
    stop = true;
}

int main( int argc, char* argv[] )
{
    signal( SIGTERM, handle_term );

    if( argc <= 3 )
    {
        printf( "usage: %s ip_address port_number backlog\n", basename( argv[0] ) );
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi( argv[2] );
    int backlog = atoi( argv[3] );

    //创建一个socket PF_INET:ipv4   SOCK_STREAM:流服务
    int sock = socket( PF_INET, SOCK_STREAM, 0 );
    //判断是否失败  失败返回-1并设置errno  成功返回socket文件描述符
    assert( sock >= 0 );

    //创建一个专用 IPV4 socket 地址
    struct sockaddr_in address;
    //bzero功能:置字节字符串address的前sizeof( address )个字节为零且包括‘\0’
    bzero( &address, sizeof( address ) );  
    address.sin_family = AF_INET;//地址组
    //inet_pton:将用字符串表示ip地址转换成网络字节序整数表示的ip地址。 成功返回1 失败返回0
    inet_pton( AF_INET, ip, &address.sin_addr );
    //小端转大端 、主机字节序转换网络字节序
    address.sin_port = htons( port );
    
    //命名socket   将address所指的地址分配给未命名的 sock 变量(文件描述符)
    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
    // bind 成功返回0 失败返回-1 并设置errno
    assert( ret != -1 );

    //监听socket sock参数:指定被监听的socket   backlog参数:提示内核监听队列的最大长度
    ret = listen( sock, backlog );
    //listen 成功返回0  失败返回-1 并设置errno
    assert( ret != -1 );

    //循环等待连接,直到有SIGTERM信号将他中断。
    while ( ! stop )
    {
        sleep( 1 );
    }

    //关闭socket
    close( sock );
    return 0;
}

在B主机

./程序名 192.168.30.109 12345 5

记得防火墙开启12345端口(如果是afw)

season@ubuntu:~$ sudo ufw allow 12345

主机A执行十次:

telnet 192.168.30.109 12345

发现执行6次后,一直处于等待中。
主机B:

netstat -nt |grep 12345

出现6(backlog值+1)个ESTABLISHED

accept用法

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


int main(int argc, char *argv[]) {
    if (argc <= 2) {
        printf("usage: %s id_address prot_number \n", basename(argv[0]));
        return 1;
    }


    const char *ip = argv[1];
    //字符串转换成整型数的一个函数
    int port = atoi(argv[2]);

    //创建一个专用 IPV4 socket 地址
    struct sockaddr_in address;
    //bzero功能:置字节字符串address的前sizeof( address )个字节为零且包括‘\0’
    bzero(&address, sizeof(address));
    address.sin_family = AF_INET;//地址组
    //inet_pton:将用字符串表示ip地址转换成网络字节序整数表示的ip地址。 成功返回1 失败返回0
    inet_pton(AF_INET, ip, &address.sin_addr);
    //小端转大端 、主机字节序转换网络字节序
    address.sin_port = htons(port);


    //创建一个socket PF_INET:ipv4   SOCK_STREAM:流服务
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    //判断是否失败  失败返回-1并设置errno  成功返回socket文件描述符
    assert(sock >= 0);

    //命名socket   将address所指的地址分配给未命名的 sock 变量(文件描述符)
    int ret = bind(sock, (struct sockaddr *) &address, sizeof(address));
    // bind 成功返回0 失败返回-1 并设置errno
    assert(ret !=-1);

    //监听socket sock参数:指定被监听的socket   backlog参数:提示内核监听队列的最大长度
    ret = listen(sock,5);
    //listen 成功返回0  失败返回-1 并设置errno
    assert(ret !=-1);

    sleep(20);

    //创建一个专用 IPV4 socket 地址
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof(client);
    int connfd = accept(sock,(
    struct sockaddr*)&client ,&client_addrlength);
    if (connfd < 0)
    {
        printf("error is :%d\n",errno);
    }
    else
    {
        char remote[INET_ADDRSTRLEN];
        printf("connected with ip: %s and port: %d \n",
               inet_ntop(AF_INET,&client.sin_addr,remote,INET_ADDRSTRLEN),ntohs(client.sin_port));
        close(connfd);
    }

    //关闭socket
    close(sock);
    return 0;
}

相关文章

网友评论

      本文标题:第五章|Linux网络编程基础API

      本文链接:https://www.haomeiwen.com/subject/qzhycftx.html