网络编程常用函数的封装 from 黑马程序员

1.socket通信函数的封装

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.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;

	if ((n = connect(fd, sa, salen)) < 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;
}

代码中的知识点:

1.exit

  • exit(0)表示程序正常退出;除了0之外,其他参数均代表程序异常退出,如:exit(1),exit(-1)。
  • return与exit的区别:return是语言级别的,它表示了调用堆栈的返回;而exit是系统调用级别的,它表示了一个进程的结束。

2.perror

3.Accept函数中的(errno == ECONNABORTED) || (errno == EINTR),即问题:为什么信号中断时,就不能终止读取?
答:由于没有东西可以读时,就会阻塞在那里,但是却由于信号过来了,本进程要中断阻塞状态去处理别的事件。这时候此阻塞状态就会被打断。
所以我们应该让进程处理完中断信号后,继续处于待读数据的阻塞状态。

  • ECONNABORTED:在TCP连接过程中,客户端意外关闭了连接。
  • EINTR:在执行一个系统调用时,被一个信号中断。

2.黏包

黏包:接收到两个数据包,但是不清楚从哪开始是一个数据包,从哪开始是另一个数据包。
黏包解决方法:

  • 约定好,一次发送固定的字节数
  • 数据的结尾加一个标记
  • 头部加上数据的大小

要实现上述功能,就有必要实现读取固定字节大小的函数,如下所示:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>


// 从fd中读取nbytes个字节,并用ptr指向
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;
}

// 向fd中写入nbytes个字节,并用ptr指向带写入数据
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;
}

/*应该读取固定的字节数数据*/
ssize_t Readn(int fd, void *vptr, size_t n)
{
	size_t  nleft;              //usigned int 剩余未读取的字节数
	ssize_t nread;              //int 实际读到的字节数
	char   *ptr;

	ptr = vptr;                // 当前读取的位置
	nleft = 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;
}

/*:固定的字节数数据*/
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;
}

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) {
			if (errno == EINTR)
				goto again;
			return -1;
		} else if (read_cnt == 0) // 当read_cnt为零时,说明fd中无数据,只有一个头部——这说明客户端发送了关闭报文
			return 0;
		read_ptr = read_buf;
	}
	read_cnt--;
	*ptr = *read_ptr++;

	return 1;
}

// 读取一行,即遇到\n就结束
ssize_t Readline(int fd, void *vptr, size_t maxlen) // 读到的数据存储在vptr中
{
	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; // 最后加上个0

	return n;
}

3.完整代码

本文的完整代码见:wrap.hwrap.c

posted @ 2022-05-08 14:03  好人~  阅读(38)  评论(0编辑  收藏  举报