一、《UNIX网络编程》——概述

一、源码编译

1、源码下载

2、编译

$tar zxvf unpv13e.tar.gz

$cd unpv13e

参照README进行编译

二、实例随笔

数据从一层传到相应层,要经历封装->封装->封装->解封->解封->解封

1、一个简单的时间获取客户端程序

#include    "unp.h"

int
main(int argc, char **argv)
{
    int                    sockfd, n;
    char                recvline[MAXLINE + 1];
    struct sockaddr_in    servaddr;

    if (argc != 2)
        err_quit("usage: a.out <IPaddress>");

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)    //建立socket连接,AF_INET:IPv4 SOCK_STREAM:字节流套接字    后面章节会详细介绍
        err_sys("socket error");

    bzero(&servaddr, sizeof(servaddr));                        //对地址结构初始化
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(13);    /* daytime server */
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
        err_quit("inet_pton error for %s", argv[1]);

    if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)    //向目的端发送连接请求
        err_sys("connect error");

    while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
        recvline[n] = 0;    /* null terminate */
        if (fputs(recvline, stdout) == EOF)                    //将发送过来的数据打印到标准输出
            err_sys("fputs error");
    }
    if (n < 0)
        err_sys("read error");

    exit(0);
}

相应重要数据结构

struct sockaddr_in
 
{
 
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
 
unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
 
struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/
 
unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
 
};

 

我的Makefile(这里不作多余解释)

其中 "unp.h" 我直接从源代码中(unpv12e/lib/unp.h)拷贝到我自己的目录中(myunp/lib/unp.h)

编译后运行

这里会报错(connect error),原因是我们在本地并没有开启端口为13的时间服务(莫急,后面会有相应的服务端代码)

----

关于IPv6的版本,就是把sockaddr_in 变成了 sockaddr_in6 其AF_INET 改成 AF_INET6

#include    "unp.h"

int
main(int argc, char **argv)
{
    int                    sockfd, n;
    struct sockaddr_in6    servaddr;
    char                recvline[MAXLINE + 1];

    if (argc != 2)
        err_quit("usage: a.out <IPaddress>");

    if ( (sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
        err_sys("socket error");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin6_family = AF_INET6;
    servaddr.sin6_port   = htons(13);    /* daytime server */
    if (inet_pton(AF_INET6, argv[1], &servaddr.sin6_addr) <= 0)
        err_quit("inet_pton error for %s", argv[1]);

    if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
        err_sys("connect error");

    while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
        recvline[n] = 0;    /* null terminate */
        if (fputs(recvline, stdout) == EOF)
            err_sys("fputs error");
    }
    if (n < 0)
        err_sys("read error");

    exit(0);
}

 

----

2、包裹函数

在不用包裹函数的代码中,经常要出现错误输出处理的代码,比如

包裹函数对其进行封装,用一个简单的函数来完成了错误处理,这里是把开头字母换成了大写

3、时间获取服务端程序

#include    "unp.h"
#include    <time.h>

int
main(int argc, char **argv)
{
    int                    listenfd, connfd;    //监听文件描述符 连接文件描述符
    struct sockaddr_in    servaddr;            
    char                buff[MAXLINE];
    time_t                ticks;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family      = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port        = htons(13);    /* daytime server */

    Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));    //把本地协议地址富裕一个套接字

    Listen(listenfd, LISTENQ);                //unp.h => LISTENQ=1024

    for ( ; ; ) {
        connfd = Accept(listenfd, (SA *) NULL, NULL);    //用于从已完成连接队列对头返回下一个已完成连接

        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));//获取时间信息,存入缓冲区
        Write(connfd, buff, strlen(buff));

        Close(connfd);
    }
}

编译运行

这样服务端程序就开启了,现在咱们就可以再运行第一个获取时间的客户端程序

获取到了时间信息

 

以上。

posted @ 2015-11-16 17:12  上帝与面包  阅读(157)  评论(1编辑  收藏  举报