今天继续探索《Unix网络编程》的框架。并改善上次的程序
在第一次接触中,我将error.h头文件分出,但是发现错误检测和输出大量的存在,因此归入到common.h中。
加入了一些安全包装函数,就是对于原函数的调用并进行错误检查。
用新的函数改善了daytimecpcli,并学习服务器程序daytimetcpsrv的编写。
目录结构如下:
各个文件的作用为:
common.h:公共头文件,包含一些常量的定义和函数的声明以及常用头文件
error.c:错误输出函数的定义
wrapsock.c:socket API 的安全封装函数的定义
wrapstdio.c:Standard I/O 的安全封装函数的定义
wrapunix.c:Unix 标准API的安全封装函数的定义
error.c见上一次的日记
下面是其它文件的内容:
common.h:
#ifndef __OUR_COMMON_HDR_H #define __OUR_COMMON_HDR_H #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #define MAXLINE 4096 #define SA struct sockaddr #define LISTENQ 1024 /* 2nd argument to listen() */ /* socket wrapper functions */ int Accept(int, SA *, socklen_t *); void Bind(int, const SA *, socklen_t); void Connect(int, const SA *, socklen_t); void Listen(int, int); int Socket(int, int, int); /* Unix wrapper functions */ ssize_t Read(int, void *, size_t); void Write(int, void *, size_t); void Close(int); /* Std I/O wrapper functions */ void Fputs(const char *, FILE *); /* Error output functions */ void err_quit(const char *fmt, ...); void err_ret(const char *fmt, ...); void err_sys(const char *fmt, ...); void err_dump(const char *fmt, ...); void err_msg(const char *fmt, ...); #endif
wrapsock.c:
#include "common.h" int Accept(int fd, SA *sa, socklen_t *salenptr) { int n; again: if ((n = accept(fd, sa, salenptr)) < 0) { #ifdef EPROTO if (errno == EPROTO || errno == ECONNABORTED) #else if (errno == ECONNABORTED) #endif goto again; else err_sys("accept error"); } return n; } void Bind(int fd, const SA *sa, socklen_t salen) { if (bind(fd, sa, salen) < 0) err_sys("bind error"); } void Connect(int fd, const SA *sa, socklen_t salen) { if (connect(fd, sa, salen) < 0) err_sys("connect error"); } void Listen(int fd, int backlog) { char *ptr; if ((ptr = getenv("LISTENQ")) != NULL) backlog = atoi(ptr); if (listen(fd, backlog) < 0) err_sys("listen error"); } int Socket(int family, int type, int protocol) { int n; if ((n = socket(family, type, protocol)) < 0) err_sys("socket error"); return n; }
wrapunix.c:
#include "common.h" ssize_t Read(int fd, void *ptr, size_t nbytes) { ssize_t n; if ((n = read(fd, ptr, nbytes)) == -1) err_sys("read error"); return n; } void Write(int fd, void *ptr, size_t nbytes) { if (write(fd, ptr, nbytes) != nbytes) err_sys("write error"); } void Close(int fd) { if (close(fd) == -1) err_sys("close error"); }
wrapstdio.c
#include "common.h" void Fputs(const char *ptr, FILE *stream) { if (fputs(ptr, stream) == EOF) err_sys("fputs error"); }
目前为止就是这样了,慢慢用到的函数再回来添加。
用上这些函数后,可以简化程序:
daytimecpcli.c:
#include "common.h" int main(int argc, char **argv) { int sockfd, n; char recvline[MAXLINE + 1]; struct sockaddr_in servaddr; if (argc != 2) err_quit("usage: ./daytimecpcli.c <IP address>"); sockfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(IPPORT_DAYTIME); if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) err_quit("inet_pton error for %s", argv[1]); Connect(sockfd, (SA *) &servaddr, sizeof(servaddr)); while ((n = Read(sockfd, recvline, MAXLINE)) > 0) { recvline[n] = 0; Fputs(recvline, stdout); } exit(0); }
daytimetcpsrv.c:
#include "common.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(IPPORT_DAYTIME); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); for (;;) { connfd = Accept(listenfd, (SA *) NULL, NULL); ticks = time(NULL); snprintf(buff, sizeof(buff), "%s\r\n", ctime(&ticks)); Write(connfd, buff, strlen(buff)); Close(connfd); } }
执行效果为:
时间: 2024-10-11 06:31:38