socket网络编程——错误函数封装

错误封装函数

系统调用不能保证每次都成功,必须进行出错处理,这样一方面可以保证程序逻辑正常,另一方面可以迅速得到故障信息。 为使错误处理的代码不影响主程序的可读性,我们把与socket相关的一些系统函数加上错误处理代码包装成新的函数,做成一个模块



错误函数源码

wrap.c

#include "wrap.h"
/* 打印错误原因并退出 */
void perr_exit(const char *s)
{
    perror(s);
    exit(1);
}
 
//accept()--right:n;error:-1  /* accept出错封装 */
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
    int n;//保存用于通信的socket文件描述符
again:
    if ( (n = accept(fd, sa, salenptr)) < 0)
    {
        if ((errno == ECONNABORTED) || (errno == EINTR))
            goto again;//如果连接终止 或 被信号中断 则再试一次
        else
            perr_exit("accept error");
    }
    return n;
}
 
/* 绑定IP、端口号和socket,出错则返回错误信息 */  // bind()--right:0;error:-1
void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (bind(fd, sa, salen) < 0)
        perr_exit("bind error");
}
 
/* 客户机连接服务器函数封装,错误则打印出错信息 */  //connect()--right:0;error:-1
void Connect(int fd, const struct sockaddr *sa, socklen_t salen)

{
	 if (connect(fd, sa, salen) < 0) 
  		 perr_exit("connect error");
}
 
/* listen函数出错处理封装 */  //listen()--right:0;error-1
void Listen(int fd, int backlog)
{
    if (listen(fd, backlog) < 0)
        perr_exit("listen error");
}
 
/* 建立套接字socket出错处理封装 */   //socket()--right:n;error:-1
int Socket(int family, int type, int protocol)
{
    int n;//保存建立socket时返回的文件描述符
    if ( (n = socket(family, type, protocol)) < 0)
        perr_exit("socket error");
    return n;
}
 
/* read函数出错处理封装 */  //read()--right:n;error:-1 
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;
}



//recv()函数错误封装
ssize_t Recv(int fd,  void *ptr, size_t nbytes, int flags)
{
    ssize_t n;
 again:
    if((n=recv(fd,ptr,nbytes,flags))==-1)
    {
        if (errno == EINTR)
            goto again;//如果是由于信号被中断 则再试一次
        else if(errno ==  EWOULDBLOCK)
       			 {perr_exit("EWOULDBLOCK");}
        else if(errno == EAGAIN)
             {perr_exit("EAGAIN");}
        else
            return -1;
    }
    return n;
}


 
/* write函数出错处理封装 */  //write()--right:n;error:-1
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;
}

//send()函数出错处理封装

ssize_t Send(int fd, const void *ptr, size_t nbytes, int flags)
{
    ssize_t n;
 again:
    if((n=send(fd,ptr,nbytes,flags))==-1)
    {
        if (errno == EINTR)
            goto again;//如果是由于信号被中断 则再试一次
        else if(errno ==  EWOULDBLOCK)
       			 {perr_exit("EWOULDBLOCK");}
        else if(errno == EAGAIN)
             {perr_exit("EAGAIN");}
        else
            return -1;  
    }
    return n;
}
                            
 
/* close关闭文件出错处理封装 */  //close()--right:0;error:-1
void Close(int fd)
{
    if (close(fd) == -1)
        perr_exit("close error");
}
 
/* 至少读满n个字符再返回 */
ssize_t Readn(int fd, void *vptr, size_t n)
{
    size_t nleft;   //剩下的字符数
    ssize_t nread;  //已读的字符数
    char *ptr;      //读写指针
 
    /* 初始化 */
    ptr = 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;//记录每次读完,还剩的未读的字节数
        ptr += nread;  //为将下一次读到的值补充到这次的末尾做准备
    }
    return n - nleft;  //返回已读到的字节数
}
 
/* 写满n个字符才返回 */
ssize_t Writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;       //剩下还要写的字节个数
    ssize_t nwritten;   //已经写的字节个数
    const char *ptr;    //写指针
 
    /* 初始化 */
    ptr = 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;
}
 
/*返回第一次调用时字符串的一个字符,调用一次返回下一个字符,供下面的函数调用*/
/* fd为要读的文件 ptr为要返回的字符(为传出参数) */
static ssize_t my_read(int fd, char *ptr)
{
    /* 定义静态变量,上一次调用的值会被保存 */
    static int read_cnt;    //剩下待返回的字符串的长度,系统将其初始化为0
    static char *read_ptr;  //当前读到的位置
    static char read_buf[128];  //读到的字符串
 
    if (read_cnt <= 0)
    {
again:
        if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0)
        {
            if (errno == EINTR)//如果是被信号中断 则再试一次
                goto again;
            return -1;
        } else if (read_cnt == 0)//如果读到的字节数为0(即读到的文件为空)
            return 0;
 
        /* 否则有读到内容 则将读指针指向该字符串的首部 */
        read_ptr = read_buf;
    }
 
    //返回当前指向的字符,之后指向下一个字符,供下一调用时使用
    *ptr = *read_ptr++;
    read_cnt--;//剩下待返回的字符串的长度减一
    return 1;//调用成功返回1
}
 
/* 一次读一行 */
ssize_t Readline(int fd, void *vptr, size_t maxlen)
{
    ssize_t n, rc;
    char c, *ptr;
 
    ptr = vptr;
    for (n = 1; n < maxlen; n++) {
        if ( (rc = my_read(fd, &c)) == 1)//获取一个字符
        {
            *ptr++ = c;
            if (c == '\n')//如果是换行就退出
                break;
        } else if (rc == 0)//如果读完了就返回读到的字节数
        {
            *ptr = 0;
            return n - 1;
        } else
            return -1;
    }
    *ptr = 0;
    return n;
}

wrap.h

#ifndef __WRAP_H_
#define __WRAP_H_

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

void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
void Bind(int fd, const struct sockaddr *sa, socklen_t salen);
void Connect(int fd, const struct sockaddr *sa, socklen_t salen);
void Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Recv(int fd, void *ptr, size_t nbytes, int flags);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
ssize_t Send(int fd, const void *ptr, size_t nbytes, int flags);
void Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
static ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);
#endif

server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>  //IP转换函数
#include <ctype.h>  //toupper函数头文件
#include <string.h>

#include "wrap.h"  // 

#define MAXLINE 800
#define SERV_PORT 6666

int main(void)
{
        struct sockaddr_in servaddr, cliaddr;
        socklen_t cliaddr_len;
        int listenfd, connfd;
        char buf[MAXLINE];
        char str[INET_ADDRSTRLEN];
        int i, n;

        listenfd = Socket(AF_INET, SOCK_STREAM, 0);


        /*
        bzero函数
        函数原型:void bzero(void *s,int n)
        作用:bzero函数的作用是将s指针指向的地址的前n个字节清零。
        */
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(SERV_PORT);

        Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        Listen(listenfd, 20);

	    printf("Accepting connections ...\n");
		cliaddr_len=sizeof(cliaddr);
		connfd=Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);

	 	while (1)
			{
				n=Read(connfd,buf,MAXLINE);
				if(n==0)
				{break;}
                printf("Data from Client is:%s",buf);
                for(i=0;i<n;i++)
                  buf[i]=toupper(buf[i]);
				Write(connfd, buf, n);
				memset(buf,0,sizeof(buf));
			}
		Close(listenfd);
		Close(connfd);
		return 0;
}

client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>

#include "wrap.h"

#define MAXLINE 800
#define SERV_PORT 6666

int main(void)
{
        struct sockaddr_in servaddr;
        char buf[MAXLINE];
        int sockfd, n;

        sockfd = socket(AF_INET, SOCK_STREAM, 0);

        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        inet_pton(AF_INET, "127.127.0.1", &servaddr.sin_addr);
        servaddr.sin_port = htons(SERV_PORT);

        connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

        while(1)
        {
        fgets(buf,sizeof(buf),stdin);
        write(sockfd,buf, strlen(buf));

        read(sockfd, buf, MAXLINE);
        printf("Response from server:%s",buf);
		memset(buf,0,sizeof(buf));

        }
        close(sockfd);
        return 0;
}

运行结果附图:
在这里插入图片描述

联合编译

以上代码生成可执行文件的方法是在编译运行时输入:

gcc server.c  wrap.c -o server

联合编译——多源文件的编译方法(test.c、testfun.c)

1.多个源文件一起编译,用法:

gcc testfun.c test.c -o test

作用:将testfun .c 和test.c 分别编译后链接成test可执行文件

2.分别编译各个源文件,之后对编译后输出的目标文件进行链接,用法:

gcc -c testfun.c //将testfun.c编译成testfun.o
gcc -c test.c  //将test.c 编译成test.o
gcc -o testfun.o test.o -o test //将testfun.o和test.o链接成test

区别:第一中方法编译时需要所有文件重新编译,而第二种方法可以只重新编译修改的文件,未修改的文件不用重新编译。

3.若要编译的文件在同一个目录下,可以用通配符【gcc *.c -o】来进行编译

4.针对一个项目下存在众多文件时,需要将上述编译过程写进一个文本文件中,即linux下的makefile

PS:接下来将详细介绍,多进程/线程的数据传输:
多进程/多线程通信实例

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值