·回顾
在 008.一个简单的网络服务器开发----回声服务器中所实现的服务器功能简单,简单到几乎没有什么错误处理,我们知道,系统调用不能保证每次都成功。如 010所示,如果我们因为自己代码写的有些问题那么光是排查错误就够我们受的了。出错的可能千千万,所以我们必须进行出错处理,这样一方面可以保证我们的程序逻辑正常,另一方面可以迅速定位到故障信息。
·错误处理函数
为了使错误处理的代码不影响主程序的可读性,我们封装的 socket相关的系统函数加上错误处理代码包装秤新的函数,做成一个新的模块,名为 error_handling
该错误处理函数的优势:
- 完美适配socket系列函数,不会给编程人员造成记忆压力。
- 函数完美配合manpage,忘记的函数可以直接在程序中直接进入到manpage中进行察看用法。
- 各个函数参数均与网络编程所需要的参数一致,内置错误打印函数,增加了程序的可读性与维护性。
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:
成功!!!