011.socket函数错误封装处理

·回顾

008.一个简单的网络服务器开发----回声服务器中所实现的服务器功能简单,简单到几乎没有什么错误处理,我们知道,系统调用不能保证每次都成功。如 010所示,如果我们因为自己代码写的有些问题那么光是排查错误就够我们受的了。出错的可能千千万,所以我们必须进行出错处理,这样一方面可以保证我们的程序逻辑正常,另一方面可以迅速定位到故障信息。

·错误处理函数

为了使错误处理的代码不影响主程序的可读性,我们封装的 socket相关的系统函数加上错误处理代码包装秤新的函数,做成一个新的模块,名为 error_handling

该错误处理函数的优势:

  1. 完美适配socket系列函数,不会给编程人员造成记忆压力。
  2. 函数完美配合manpage,忘记的函数可以直接在程序中直接进入到manpage中进行察看用法。
  3. 各个函数参数均与网络编程所需要的参数一致,内置错误打印函数,增加了程序的可读性与维护性。

error_handling.h

#pragma once
#ifndef __ERROR_HANDING__
#define __ERROR_HANDING__

#include<unistd.h>

//下面的所有替换socket网络编程函数,在函数内部都增加了输出错误信息的判断语句。

//打印输出错误信息
void perr_exit(const char* s);	

//替换Socket函数,并在内部调用perr_exit 函数,可以输出错误信息
int Socket(int family, int type, int protocol);

//替换Bind函数,并在内部调用perr_exit 函数,可以输出错误信息
int Bind(int fd, const struct sockaddr* sa, socklen_t salen);

//替换Listen函数,并在内部调用perr_exit 函数,可以输出错误信息
int Listen(int fd, int backlog);

//替换 accept 函数,并在内部调用perr_exit 函数,可以输出错误信息
int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr);

//替换Connect函数,并在内部调用perr_exit 函数,可以输出错误信息
int Connect(int fd, const struct sockaddr* sa, socklen_t salen);

//替换Close函数,并在内部调用perr_exit 函数,可以输出错误信息
int Close(int fd);

ssize_t Read(int fd, void* ptr, size_t nbytes);
ssize_t Write(int fd, const void* ptr, size_t nbytes);
ssize_t Readn(int fd, void* vptr, size_t n);
ssize_t Writen(int fd, const void* vptr, size_t n);
ssize_t my_read(int fd, char* ptr);
ssize_t Readline(int fd, void* vptr, size_t maxlen);

#endif

error_handling.cpp

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>

void perr_exit(const char* s)
{
	perror(s);	//输出打印信息。
	exit(-1);
}

int Accept(int fd, struct sockaddr* sa, socklen_t* salenptr)
{
	int n;

again:
	if ((n = accept(fd, sa, salenptr)) < 0) {
		if ((errno == ECONNABORTED) || (errno == EINTR))
			goto again;
		else
			perr_exit("accept error");
	}
	return n;
}

int Bind(int fd, const struct sockaddr* sa, socklen_t salen)
{
	int n;

	if ((n = bind(fd, sa, salen)) < 0)
		perr_exit("bind error");

	return n;
}

int Connect(int fd, const struct sockaddr* sa, socklen_t salen)
{
	int n;
	n = connect(fd, sa, salen);
	if (n < 0) {
		perr_exit("connect error");
	}

	return n;
}

int Listen(int fd, int backlog)
{
	int n;

	if ((n = listen(fd, backlog)) < 0)
		perr_exit("listen error");

	return n;
}

int Socket(int family, int type, int protocol)
{
	int n;

	if ((n = socket(family, type, protocol)) < 0)
		perr_exit("socket error");

	return n;
}

ssize_t Read(int fd, void* ptr, size_t nbytes)
{
	ssize_t n;

again:
	if ((n = read(fd, ptr, nbytes)) == -1) {
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}

	return n;
}

ssize_t Write(int fd, const void* ptr, size_t nbytes)
{
	ssize_t n;

again:
	if ((n = write(fd, ptr, nbytes)) == -1) {
		if (errno == EINTR)
			goto again;
		else
			return -1;
	}
	return n;
}

int Close(int fd)
{
	int n;
	if ((n = close(fd)) == -1)
		perr_exit("close error");

	return n;
}

/*参三: 应该读取的字节数*/  
//socket 4096  readn(cfd, buf, 4096)   nleft = 4096-1500
ssize_t Readn(int fd, void* vptr, size_t n)
{
	size_t  nleft;              //usigned int 剩余未读取的字节数
	ssize_t nread;              //int 实际读到的字节数
	char* ptr;

	ptr = (char*)vptr;
	nleft = n;                  //n 未读取字节数

	while (nleft > 0) {
		if ((nread = read(fd, ptr, nleft)) < 0) {
			if (errno == EINTR)
				nread = 0;
			else
				return -1;
		}
		else if (nread == 0)
			break;

		nleft -= nread;   //nleft = nleft - nread 
		ptr += nread;
	}
	return n - nleft;
}

ssize_t Writen(int fd, const void* vptr, size_t n)
{
	size_t nleft;
	ssize_t nwritten;
	const char* ptr;

	ptr = (char*)vptr;
	nleft = n;
	while (nleft > 0) {
		if ((nwritten = write(fd, ptr, nleft)) <= 0) {
			if (nwritten < 0 && errno == EINTR)
				nwritten = 0;
			else
				return -1;
		}
		nleft -= nwritten;
		ptr += nwritten;
	}
	return n;
}

static ssize_t my_read(int fd, char* ptr)
{
	static int read_cnt;
	static char* read_ptr;
	static char read_buf[100];

	if (read_cnt <= 0) {
	again:
		if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {   //"hello\n"
			if (errno == EINTR)
				goto again;
			return -1;
		}
		else if (read_cnt == 0)
			return 0;

		read_ptr = read_buf;
	}
	read_cnt--;
	*ptr = *read_ptr++;

	return 1;
}

/*readline --- fgets*/
//传出参数 vptr
ssize_t Readline(int fd, void* vptr, size_t maxlen)
{
	ssize_t n, rc;
	char    c, * ptr;
	ptr = (char*)vptr;

	for (n = 1; n < maxlen; n++) {
		if ((rc = my_read(fd, &c)) == 1) {   //ptr[] = hello\n
			*ptr++ = c;
			if (c == '\n')
				break;
		}
		else if (rc == 0) {
			*ptr = 0;
			return n - 1;
		}
		else
			return -1;
	}
	*ptr = 0;

	return n;
}

这样属于我们自己的错误处理函数就封装好了,下一篇博客我们将会把我们之前写的简单回声服务器进行改造,这样我们就能实现一个非常完美的回声服务器了,系统能够自动排出部分错误,再也不用像之前一样因为一个connect函数的返回值而被困在程序很久的尴尬情况。

测试进入manpage:

在这里插入图片描述
shift+k:进入manpage:

在这里插入图片描述
成功!!!

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值