UNP第一章 简介TCP和IP

概述

简介

Web客户与服务器之间使用传输层的TCP协议通信。TCP转而使用网络层的IP协议通信,IP再通过某种形式的数据链路层通信。
这里写图片描述 客户和服务器通常是用户进程,而TCP和IP协议通常是内核中协议栈的一部分。
包裹函数:用于检查错误、输出适当的消息,以及在出错时终止程序的运行。

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

Xcode导入unp.h

  • 修改项目Build settings中的Header Search Path和Library Search
    Path,使填写的目录下包含unp.h,config.h, apue.h和error.c。这两个文件的获取需通过下载unpv13e;
  • 修改unp.h文件中的#include “../config.h” 为#include “config.h”
  • 在新建的.c文件中 #include “unp.h”和#include “apue.h”,同时在apue.h中添加error.c。这里导入apue.h的原因是要用到错误处理函数err_xxx()。

时间获取– 客户端程序

  1. 利用Mac OS的Xcode运行此程序时,首先要进入root模式,获取最高权限,程序编译时会提示Bind Error:Permission Denied。
  2. 在wrapsock.c中需要添加包裹函数Write()和Close():
void Write(int fd, void *ptr, size_t nbytes)
{
    if (write(fd, ptr, nbytes) != nbytes)
        err_quit("write error");
}

void Close(int fd)
{
    if (close(fd) == -1)
        err_quit("close error");
}

3.编译时如果遇到err_quit()等类似err_xxx()函数报错,则需要修改error.c中的具体函数。查看err_xxx()是否存在于error.c中,error.c源代码如下:

#include "apue.h"
#include <errno.h>      /* for definition of errno */
#include <stdarg.h>     /* ISO C variable aruments */

static void err_doit(int, int, const char *, va_list);

/*
 * Nonfatal error related to a system call.
 * Print a message and return.
 */
void
err_ret(const char *fmt, ...)
{
    va_list     ap;

    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
}

/*
 * Fatal error related to a system call.
 * Print a message and terminate.
 */
void 
err_sys(const char *fmt, ...)
{
    va_list     ap;

    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    exit(1);
}

/*
 * Nonfatal error unrelated to a system call.
 * Error code passed as explict parameter.
 * Print a message and return.
 */
void
err_cont(int error, const char *fmt, ...)
{
    va_list     ap;

    va_start(ap, fmt);
    err_doit(1, error, fmt, ap);
    va_end(ap);
}

/*
 * Fatal error unrelated to a system call.
 * Error code passed as explict parameter.
 * Print a message and terminate.
 */
void
err_exit(int error, const char *fmt, ...)
{
    va_list     ap;

    va_start(ap, fmt);
    err_doit(1, error, fmt, ap);
    va_end(ap);
    exit(1);
}

/*
 * Fatal error related to a system call.
 * Print a message, dump core, and terminate.
 */
void
err_dump(const char *fmt, ...)
{
    va_list     ap;

    va_start(ap, fmt);
    err_doit(1, errno, fmt, ap);
    va_end(ap);
    abort();        /* dump core and terminate */
    exit(1);        /* shouldn't get here */
}

/*
 * Nonfatal error unrelated to a system call.
 * Print a message and return.
 */
void
err_msg(const char *fmt, ...)
{
    va_list     ap;

    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
}

/*
 * Fatal error unrelated to a system call.
 * Print a message and terminate.
 */
void 
err_quit(const char *fmt, ...)
{
    va_list     ap;

    va_start(ap, fmt);
    err_doit(0, 0, fmt, ap);
    va_end(ap);
    exit(1);
}

/*
 * Print a message and return to caller.
 * Caller specifies "errnoflag".
 */
static void
err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
    char    buf[MAXLINE];

    vsnprintf(buf, MAXLINE-1, fmt, ap);
    if (errnoflag)
        snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
          strerror(error));
    strcat(buf, "\n");
    fflush(stdout);     /* in case stdout and stderr are the same */
    fputs(buf, stderr);
    fflush(NULL);       /* flushes all stdio output streams */
}

4.若在Centos下运行也比较麻烦,我就试了一下,结果很多缺少很多unix的库函数:

客户端程序

#include "unp.h"
#include "apue.h"
#include <time.h>
#include "wrapsock.c"

int main()
{
    int                 sockfd, n;
    char                recvline[MAXLINE + 1];
    struct sockaddr_in  servaddr;
    char *argv = "192.168.0.105";
    printf("Client is on\n");
    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        err_sys("socket error");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(13);    //置端口号为13,时间获取服务器的端口
    if (inet_pton(AF_INET, argv, &servaddr.sin_addr) <= 0)
        err_quit("inet_pton error for %s", argv);

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

    while ( (n = (int)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);
}

服务器端程序

#include "unp.h"
#include "apue.h"
#include <time.h>
#include "wrapsock.c"
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));//bind出错

    Listen(listenfd, LISTENQ);
    printf("Server is on\n");
    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);
    }

}

运行结果

  1. 首先运行daytimetcpsrv.c, 然后再运行daytimetcpcli.c,在客户端程序中,需要把ip地址参数设置本机的ip地址。

    这里写图片描述

  2. 同样的程序也可以在终端里执行。需要把上述的4个头文件与.c文件放在一个文件目录下。注意,需要用su -切换至root用户执行。执行结果如图:
    这里写图片描述

心得

1.Bind Error:Permission Denied:这个错误是因为文件权限不足导致的。解决办法:若要使用Xcode编译,则需要利用root用户登录Mac;使用gcc编译,需要切换至root用户su -
2.Bind Error:Address already in use:这个错误是之前服务器程序已打开,而现在无法继续进行bind操作。解决方法:可以kill掉服务器进程。例如,daytimetcpsrv.c指定使用端口13,那么我们在命令行输入lsof -i:13可以查看相关使用该端口的进程pid,然后利用kill pid命令终止进程,就可以解决这个error。

错误处理:包裹函数

任何现实世界的程序都必须检查每个函数调用是否返回错误。因此上文引入error.c是相当必要的。error.c中定义了err_quit和err_sys函数输出一个出错信息并退出程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值