Unix网络编程学习日记 (2)

今天继续探索《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

Unix网络编程学习日记 (2)的相关文章

Unix网络编程学习日记

今天开始拜读<Unix网络编程>.找到的源代码在Linux下有各种问题,最后决定还是自己从头写比较好. 从第一个时间服务程序开始学习.今天先看一下主要的头文件的作用. 在common.h中(参照 unp.h 自己写的,包含常用头文件和一些常量定义,用着方便),有以下的头文件: sys/types.h 此头文件是系统类型的定义,如:int8_t int16_t int32_t int64_t等等 sys/socket.h 这是socket的接口,在其中引入bits/socket.h,其中定义了各

Unix网络编程学习笔记之第2章 TCP和UDP

TCP 1. TCP面向连接的协议,是一个字节流协议,没有任何记录边界.发送的是数据分组. 2. TCP提供了可靠性:确认重传和重组 (1) TCP每发送一份数据都会要求对端进行确认.如果超时,就会重传.TCP会估计往返时间RTT,以确定等待多长时间重传. (2) 如果多次发送数据分组,TCP可以保证分组的按序达到.即会根据序列号进行重组. 3. TCP提供流量控制 TCP在任何时刻通知对端,它此时一次能够接受多少字节的数据,即通告窗口.该窗口指出接受缓冲区当前可用的空间. 4. 为何说TCP是

Unix网络编程学习笔记之第7章 套接字选项

一.获取/设置套接字选项的方法 一个套接字描述符相关联的套接字选项很多.获取/设置套接字选项的方法: 1.  getsockopt和setsockopt函数 2. fcntl函数 3. ioctl函数 二. getsockopt和setsockopt函数 int getsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen); int setsockopt(int sockfd, int level,

Unix网络编程学习笔记之第6章 I/O复用:select和poll函数

一.I/O复用应用场合 1. 当客户处理多个描述符(既有标准输入,又有网络套接字)时,必须使用IO复用. 2. 一个客户同时处理多个套接字是可能的. 3. 如果一个服务器既要处理监听套接字,又要处理已连接套接字,一般就要使用I/O复用. 4. 如果一个服务器既要处理TCP,又要处理UDP,一般就要I/O复用. 5. 如果一个服务器要处理多个服务或协议,就要用到I/O复用. 其实IO复用就是一个进程/线程处理多个套接字描述符. 二. I/O模型 Unix提供了5种I/O模型: 1. 阻塞式I/O模

UNIX网络编程学习笔记2 需要用到的一些字节操纵和格式转换函数

当然这些东西是炒鸡无聊的,但是真当自己开始撸代码时才发现熟悉这些枯燥的函数能够节约大量的时间.于是总结一下: 字节序:低序字节存储在起始地址,这称为小端(little-endian),高序字节存储在起始地址,这称为大端(big-endian) 例:存放0x0A0B0C0D LE: 0D 0C 0B 0A BE: 0A 0B 0C 0D 小端的存放方式更加接近于人类思维 网际协议使用大端字节序来传送多字节整数(为何要规定一个字节序来传输ip和port? 呸 这样子协议才能正确“看懂”(解释)这些地

Unix网络编程学习笔记之第8章 基于UDP套接字编程

一. UDP C/S的典型函数调用 UDP没有像TCP那样的连接,客户端直接sendto向某服务器发送数据,服务器端一直recvfrom阻塞,以接收任何客户端发送的数据. 二. sendto和recvfrom函数 int sendto(int sockfd, const void* buff, size_t nbytes, int flag, const struct sockaddr* to, socklen_taddrlen); int recvfrom(int sockfd, void*

Unix网络编程学习笔记之第11章 名字与地址转换

一. 域名系统(DNS) 1. 简介 DNS主要用于主机名和IP地址之间的映射. 主机名可以是简单的名字ljm,也可以是全限定域名ljm.localdomainbaidu.com等. 2.资源记录 DNS中的条目称为资源记录(RR).我们感兴趣的RR类型只有几个: A             A记录把一个主机名映射为一个32位的IPv4地址. AAAA    4A记录把一个主机名映射为一个128位的IPv6地址. 例如: ljm               IN      A    127.0.

Unix网络编程学习笔记之第1章 简介

一.一个简单的时间获取客户端 #include <sys/socket.h> #define MAXCON 50 #define MAXLINE 1024 #define PORT 13 void err_sys(const char* s) { fprintf(stderr, "%s\n",s); exit(1); } int main(int argc, char** argv) { int sockfd; structsockaddr_in servaddr; cha

Unix网络编程学习笔记之第5章 TCP客户端/服务器程序示例

一. 一个简单TCP回射服务端程序 #include "unp.h" #define MAXLINE 1024 #define PORT 13 #define CONMAX 5 void err_sys(const char* s) { fprintf(stderr, "%s\n",s); exit(1); } void str_echo(int connfd) { int nbyte; char buff[MAXLINE+1]; again: while(nbyt