unix network programming(3rd)Vol.1 [第1章]《读书笔记系列》

文章最开头介绍了

  • 获取时间的C/S 模型的代码, 还用了实现了IPV6的版本

  • unix 介绍了errno值,以及在多进程/多线程中的问题

多线程中不用全局errno,而是用返回值 处理error 详细见第26章

    //可以这么写
    int n;
    if( (n=pthread_mutex_lock(&ndone_mutex))   !=0  )
       errno=n,err_sys("ptherad_mutex_lock error");// 逗号分割,不用{} 花括号
    
    //当然我们也可以封装下
    void warpper_Pthread_mutex_lock(pthread_mutex *mptr)
    {
        int n;
        if(  (n=pthread_mutex_lock(mptr) )   == 0 )
        return;
        errno = n;
        err_sys("ptherad_mutex_lock error");
    }
  • 简单介绍了,buffer溢出老问题,基本上也都是c99/c11/c++11替换老版本

    windows 是 _s系列 比如strcpy_s, memcpy_s, sprintf_s
    当然wide strings版本也有封装 wsprinf_s,wmemcpy_s
    linux 的wide strings头文件是 (wchar.h)

    printf() -> puts() Write string to stdout
    gets() -> fgets()
    sprintf() -> snprintf()
    strcat()-> strncat()->strlcat()//最好用strlcat
    strcpy()-> strncpy()->strlcpy()//最好用strlcpy

    等等

《Strlcpy和strlcat——一致的、安全的字符串拷贝和串接函数 》详细介绍了为什么 英文原版
中文版

size_t strlcpy(char *dst, const char *src, size_t size);
size_t strlcat(char *dst, const char *src, size_t size);
简单概括:
strcat、strcpy 容易溢出,
strncat、strncpy() 最后一位需要手动 置0,  (strncpy 当目标缓冲区远远大于源字符串的长度时,剩下的空闲数据都需要置0,导致性能降低) 还要strnlen去计算实际长度
以上函数的滥用的太多
所以最好使用strlcpy(),由于是openBSD,gnulibc并没有,所以有可能要自己实现。(或者自己从bsd 中弄下来)

ftp://ftp.openbsd.org/pub/OpenBSD/5.7/src.tar.gz

http://www.opensource.apple.com/ 中可以找到任意OSX版本的代码 最新的OS X 是 10.10.2

这里多插一句,顺便看了下http://www.opensource.apple.com/source/tcp_wrappers/tcp_wrappers-20/tcp_wrappers/socket.c.diff
里面修改的也就是 同时兼容 IPV4/IPV6。
代码格式命名还是和UNP 这本书中一模一样,
我也不做评论了,大家自己看吧

最新版本代码
strlcpy.c
strlcat

strcpy
strcat
strncpy
strncat
就不一一列举了,都在目录

旧版本 http://www.opensource.apple.com/source/Libc/Libc-320/string/FreeBSD/
strlcpy.3
strlcpy.c

strlcpy
    // man 手册中 A simple implementation of strncpy() might be:
           char *strncpy(char *dest, const char *src, size_t n)
           {
               size_t i;

               for (i = 0; i < n && src[i] != '\0'; i++)
                   dest[i] = src[i];
               for ( ; i < n; i++)
                   dest[i] = '\0';

               return dest;
           }

    //=========== OpenBSD  5.7 版本的strlcpy     OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp 

    /*
     * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */

    #include <sys/types.h>
    #include <string.h>

    /*
     * Copy string src to buffer dst of size dsize.  At most dsize-1
     * chars will be copied.  Always NUL terminates (unless dsize == 0).
     * Returns strlen(src); if retval >= dsize, truncation occurred.
     */
    size_t strlcpy(char *dst, const char *src, size_t dsize)
    {
	const char *osrc = src;
	size_t nleft = dsize;

	/* Copy as many bytes as will fit. */
	if (nleft != 0) {
		while (--nleft != 0) {
			if ((*dst++ = *src++) == '\0')
				break;
		}
	}

	/* Not enough room in dst, add NUL and traverse rest of src. */
	if (nleft == 0) {
		if (dsize != 0)
			*dst = '\0';		/* NUL-terminate dst */
		while (*src++)
			;
	}

	return(src - osrc - 1);	/* count does not include NUL */
    }

    //===========MACOS  Libc-1044.10.1版本的strlcpy
    #include <strings.h>
    size_t strlcpy(char * restrict dst, const char * restrict src, size_t maxlen) {
        const size_t srclen = strlen(src);
        if (srclen < maxlen) {
            memcpy(dst, src, srclen+1);
        } else if (maxlen != 0) {
            memcpy(dst, src, maxlen-1);
            dst[maxlen-1] = '\0';
        }
        return srclen;
    }

strlcat
/*
 * Copyright (c) 2011 Apple, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include <strings.h>

size_t
strlcat(char * restrict dst, const char * restrict src, size_t maxlen) {
    const size_t srclen = strlen(src);
    const size_t dstlen = strnlen(dst, maxlen);
    if (dstlen == maxlen) return maxlen+srclen;
    if (srclen < maxlen-dstlen) {
        memcpy(dst+dstlen, src, srclen+1);
    } else {
        memcpy(dst+dstlen, src, maxlen-dstlen-1);
        dst[maxlen-1] = '\0';
    }
    return dstlen + srclen;
}

习题

netstat -ni -i显示网络接口信息,n 输出数值信息
netstat -nr -r显示路由表,-n输出数值地址
ifconfig eth0 可以看到设备MTU 1500(默认)

errno 可以在 include <errno.h> 中找到对因number的意义,man page也可以找到
getaddrinfo()可以取得IPv4/IPV6 地址,从而写出IPv4/IPV6通用的 代码。
MAC OS X inet_ntop 中还是用了static变量 ,多线程 还是会出现问题(http://www.opensource.apple.com/source/Libc/Libc-1044.1.2/net/inet_pton.c)


IP地址 字符串 与 网络字节序二进制 互相转换

n numberic 1100 0000 1010 1000 0000 0001 0000 0001
a ASC II "192.168.1.1"

inet_pton inet_ntop (p 代表 presentation n 代表numberic 支持IPV4IPv6,都是封装了inet_aton/inet_ntoa/ inet_addr 这些函数,加了些判断处理)
int inet_pton(int af, const char *src, void *dst);
inet_pton - convert IPv4 and IPv6 addresses from text to binary form string,转换 字符串地址 到 binary地址

    struct sockaddr_in   servaddr_IPV4;
    struct sockaddr_in6 servaddr_IPV6;
    inet_pton(AF_INET, "192.168.1.200", &servaddr_IPV4.sin_addr);
    inet_pton(AF_INET6, argv[1],  &servaddr_IPV6.sin6_addr);  //argv[1] 是ipv6 地址

推荐用新的inet_pton、inet_ntop 同时支持IPV4/IPV6

看到了sin_addr 结构之后可以用snprintf()自己实现(其实以前不知道这个函数,自己实现过很多遍)

    /* Internet address.  */
    typedef uint32_t in_addr_t;
    struct in_addr
      {
        in_addr_t s_addr;
      };

既然知道是uint32_t 那么unsigned int 是 32bits, 0000 0000 0000 0000 每8bit代表一个段,0~255

要注意little-end, big-end 问题, 因为要转换到网络地址。
本地 x86/x86_x64 都是little-end, IBM POWER PC是big-end
网络上是big-end。
具体参见《深入理解计算机系统》最新是3rd,本书后面也有详细介绍。
On the i386 the host byte order is Least Significant Byte first,whereas the network byte order, as used on the Internet, is Most Significant Byte first.

htonl, htons, ntohl, ntohs - convert values between host and network byte order
htonl 主机字节序转换成网络字节序

       #include <arpa/inet.h>
       uint32_t htonl(uint32_t hostlong);     //host convert to network long  
       uint16_t htons(uint16_t hostshort);   //host convert to network short
       uint32_t ntohl(uint32_t netlong);       // network convert to host long
       uint16_t ntohs(uint16_t netshort);     // network convert to host short

16bit 用于 端口号
32bit 用于 IP地址(仅仅是IPV4)


posted @ 2015-07-09 21:41  scott_h  阅读(371)  评论(0编辑  收藏  举报