011.socket函数错误封装处理

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

·回顾

在 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:

20210506141304381.pngshift+k:进入manpage:

2021050614134574.png

成功!!!

目录
相关文章
|
7月前
socket字节序转换与地址转换函数记录
【代码】socket字节序转换与地址转换函数记录。
45 0
|
7月前
socket编程之回声服务器函数的陷阱
由connect函数使用不当导致的小错误 话不多说先看代码:
60 0
|
6月前
|
缓存 网络协议 Linux
c++实战篇(三) ——对socket通讯服务端与客户端的封装
c++实战篇(三) ——对socket通讯服务端与客户端的封装
155 0
|
7月前
|
存储 算法 网络协议
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
【探索Linux】P.26(网络编程套接字基本概念—— socket编程接口 | socket编程接口相关函数详细介绍 )
79 0
|
7月前
socket编程之 connect()函数
再讲了服务器端的函数调用之后,终于来到了我们的客户端编程了(read/write之后会详细介绍的),客户端编程相较于服务器端来说是非常简单的了,在掌握了服务器端编程之后再看客户端编程就会胸有成竹(只需要在学一个函数即可)。
134 0
|
7月前
socket编程之 accept函数的理解
在进入我们的正题之前,再来复习一波编写服务器的函数流程吧
408 0
|
7月前
|
网络协议 Linux 定位技术
网络编程函数小总结与初识socket
总结服务器端的函数和客户端的函数 再次声明博主写的都是对于linux下的网络编程,没有写关于Windows的网络编程,也许以后会写到。 这里只是总结一下,具体参数的含义等后面的跟新
72 0
|
网络协议
socket编程函数
socket编程函数
39 0
|
网络协议 Unix Linux
Android C++ 系列:Linux Socket 编程(二)网络套接字函数
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描 述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调 用出错则返回-1。对于IPv4,domain参数指定为AF_INET。对于TCP协议,type参数指定为 SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,表 示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。
212 0
|
存储 网络协议 Unix
socket编程相似对象、函数、概念的区别于联系
inet_addr类似于inet_aton,是将以点分开的IP地址字符串转成in_addr对象。