Linux网络编程初步

时间:2021-07-21 00:18:26

Linux网络编程初步

主机字节序和网络字节序

32位置机器一次性能装载4字节。那么四字节在内存的顺序影响它被累加器装载成的整数的值。主要分为大端和小端。

大端字节序是一个整数高位字节(23~31bit)存在内存的低处,低字节(0~7 bit) 存储在内存的高地址处。小端相反。

现代PC大多是小端序。小端就称为主机字节序。JVM采用大端(网络字节序)。下面是检测大端小段的案例

我的linux机子是小端序了。。。

    union {
        short value;
        char union_bytes[sizeof( short ) ];
    } test;
    test.value = 0x0102;
    printf("%d %d\n", test.union_bytes[0], test.union_bytes[1]);
    if(test.union_bytes[0] == 1 && test.union_bytes[1] == 2) {
        puts("大端序");
    } else if(test.union_bytes[0] == 2 && test.union_bytes[1] == 1) {
        puts("小端序");
    } else {
        puts("未知");
    }

linux下提供了四个函数来完成主机字节序和网络字节序的转换。

#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);    
unsigned long int ntohl(unsigned long int netlong); 
unsigned short int ntohs(unsigned short int netshort);    

例如:host to network long => htonl

一个网络编程的例子

服务端:

#include <bits/stdc++.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>

#define BUF_SIZE 1024


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

    const char* ip = argv[1];
    int port = atoi( argv[2] );


    // ===================================================================================================================
    // 创建套接字
    //创建地址的结构体
    struct sockaddr_in address;                 // <netinet/in.h>
                                                //* Structure describing an Internet socket address.  */
                                                //    struct sockaddr_in
                                                //    {
                                                //       __SOCKADDR_COMMON (sin_);
                                                //        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)];
                                                //    };
    bzero(&address, sizeof( address ));         // <string.h> /* Set N bytes of S to 0.  */
                                                // extern void bzero (void *__s, size_t __n) __THROW __nonnull ((1));
                                                // 里面的sin_zero数组通常清零,所以这么写
    address.sin_family = AF_INET;               // AF_INET代表IPV4地址族,PF_INET是对应的协议族,宏定义的值是一样的,理论上可以混用

    inet_pton(AF_INET, ip, &address.sin_addr);  //  <arpa/inet.h>  
                                                //
                                                //  extern int inet_pton (int __af, const char *__restrict __cp,
                                                //  void *__restrict __buf) __THROW;
                                                //
                                                //    /* Convert from presentation format of an Internet number in buffer
                                                //      starting at CP to the binary network format and store result for
                                                //      interface type AF in buffer starting at BUF.  */

    address.sin_port = htons( port );           // 大端字节序和小段字节序的转换 <netinet/in.h>

    int sock = socket(PF_INET, SOCK_STREAM, 0); //创建套接字    <sys/socket.h>
    assert( sock >= 0 );                        //* Create a new socket of type TYPE in domain DOMAIN, using
                                                //  protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically.
                                                //  Returns a file descriptor for the new socket, or -1 for errors.  */
                                                //  extern int socket (int __domain, int __type, int __protocol) __THROW;
                                                //  domain: 说明系统底层的协议族,对于TCP/IP 而言,改参数设置为PF_INET(IPV4),
                                                //          PF_INET6(IPV6), UNIX本地域协议簇,改参数设置为 PF_UNIX
                                                //  type: 服务类型 SOCK_STREAM(流服务),SOCK_UGRAM(数据报)
                                                //        对于TCP,SOCK_STREAM 表示TCP,  SOCK_DGRAM 表示
                                                // protocol: 在前面的协议前提下的可选参数,一般给0就好
    // ===================================================================================================================

    // 命名socket
    int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ));     
    assert( ret != -1 );                        // /* Give the socket FD the local address ADDR (which is LEN bytes long).  */
                                                //  extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
                                                //    __THROW;
                                                //  sock就是刚刚socket的返回值。可以理解为端口和IP绑定。
    
    ret = listen( sock, 5 );                    // 开始监听。
    assert( ret != -1 );                        // /* Prepare to accept connections on socket FD.
                                                //  N connection requests will be queued before further requests are refused.
                                                //  Returns 0 on success, -1 for errors.  */
                                                //  extern int listen (int __fd, int __n) __THROW;
                                                //  第二个参数是处于完全连接状态(ESTABLISHED)的 socket上线。
    struct sockaddr_in client;
    socklen_t client_addrlength = sizeof( client ); // types.h  是个 unsigned int

    int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength);
                                                //    This function is a cancellation point and therefore not marked with
                                                //    __THROW.  */
                                                //    extern int accept (int __fd, __SOCKADDR_ARG __addr,
                                                //            socklen_t *__restrict __addr_len);
                                                //

    if(connfd < 0)
    {
        printf("errno is: %d\n", errno);
    }
    else
    {
        char buffer[ BUF_SIZE ];

        memset( buffer, '\0', BUF_SIZE );
        ret = recv( connfd, buffer, BUF_SIZE - 1, 0 );
        printf("[%d: %s]\n", ret, buffer);

        memset( buffer, '\0', BUF_SIZE );
        ret = recv( connfd, buffer, BUF_SIZE - 1, MSG_OOB );
        printf("[%d: %s]\n", ret, buffer);

        memset( buffer, '\0', BUF_SIZE );
        ret = recv( connfd, buffer, BUF_SIZE - 1, 0 );
        printf("[%d: %s]\n", ret, buffer);

        close( connfd );

    }
    close( sock );
    return 0;
}

客户端:

#include <bits/stdc++.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    if(argc <= 2)
    {
        printf("usage: %s ip_address port_number\n", basename( argv[0] ));
        // basename :
        // Return the file name within directory of FILENAME.  We don't
        //  declare the function if the `basename' macro is available (defined
        //  in <libgen.h>) which makes the XPG version of this function
        //  available.
        return 1;
    }

    const char* ip = argv[1];
    int port = atoi( argv[2] );

    printf("ip = %s port = %d\n", ip, port);

    struct sockaddr_in server_address;
    bzero(&server_address, sizeof( server_address ));
    server_address.sin_family = AF_INET;    //#define PF_INET 2 //IP protocol family. // PF_INET equal AF_INET

    inet_pton( AF_INET, ip, &server_address.sin_addr );
    server_address.sin_port = htons( port );

    int sockfd = socket( PF_INET, SOCK_STREAM, 0 );
    assert( sockfd >= 0 );

    if( connect( sockfd, ( struct sockaddr* )&server_address, sizeof( server_address ) ) < 0 )
    {
        printf("connection failed\n");
    }
    else
    {
        const char *oob_data = "abc";
        const char *normal_data = "123";
        send(sockfd, normal_data, strlen(normal_data), 0);
        send(sockfd, oob_data, strlen(oob_data), MSG_OOB);
        send(sockfd, normal_data, strlen(normal_data), 0);
    }
    close(sockfd);
    return 0;
}