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);
	}
}

執行效果爲:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章