unix网络编程(一) 封装常用的socket函数

sever端socket连接的流程:创建socket ====> 绑定ip和端口 ====> 监听listen ====> 接受连接accept

由于上述流程是通用的,过程中还有大量的错误判断和网络字节序的转换,因此封装成一些函数方便调用。

其中accpet易受信号中断和软件中断,使用goto语句,在中断时再次accept。

端口复用:

在server的TCP连接没有完全断开之前不允许重新监听是不合理的。因为,TCP连接没有完全断开指的是connfd(127.0.0.1:6666)没有完全断开,而我们重新监听的是lis-tenfd(0.0.0.0:6666),虽然是占用同一个端口,但IP地址不同,connfd对应的是与某个客户端通讯的一个具体的IP地址,而listenfd对应的是wildcard address。解决这个问题的方法是使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。

在server代码的socket()和bind()调用之间插入如下代码:

int opt = 1;

setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

pub.c文件 

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

// 创建socket的封装, 输入ip和port,成功返回lfd,失败返回 -1
int socketBind(char *ip, int port)
{
	int lfd = 0;
	//创建套接字
	lfd = socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == lfd){
		perror("socket create");
		return -1;
	}

    int opt = 1;
    if( setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
    {
        perror("setsockopt");
        return -1;
    }    

	//绑定
	char buf[24] = "";
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	if(addr.sin_addr.s_addr = inet_ntop(AF_INET, ip, buf, sizeof(addr)) == NULL )
	{
		perror("inet_pton");
		return -1;
	}
	int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
	if(-1 == ret)
	{
		perror("bind");
		return -1;
	}

	//监听
	ret = listen(lfd, 128);
	if(-1 == ret)
	{
		perror("listen");
		return -1;
	}

	return lfd;
}

// 封装Accept,返回新的socket连接cfd。避免在accept过程中被ECONNABORTED和EINTR信号打断导致错误。
int Accept(int lfd, struct sockaddr *sa, socklen_t *salenptr)
{
	int n;
again:
	if( (n = accept(lfd, sa, salenptr)) < 0 )
	{
		if( (errno == ECONNABORTED) || (errno == EINTR) ) // 如果被信号中断或者软件层次中断不能退出。
			goto again;
		else
			perror("accept");
	}
	return n;
}

// 带打印客户端ip的accept
int Accept_with_print(int lfd)
{
	int cfd = -1;
	char ip[36] = "";
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	socklen_t len = sizeof(addr);
	cfd = Accept(lfd, (struct sockaddr*)&addr, &len);
	printf("client connected, ip=%s, port=%d\n", 
			inet_ntop(AF_INET, &addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(addr.sin_port));

	return cfd;
}

pub.h文件

// pub.h
#ifndef _PUB_H_
#define _PUB_H_ 
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<errno.h>
// 创建socket的封装, 输入ip和port,成功返回lfd,失败返回 -1
int socketBind(char *ip, int port);

// 封装Accept,返回新的socket连接cfd。避免在accept过程中被ECONNABORTED和EINTR信号打断导致错误。
int Accept(int lfd, struct sockaddr *sa, socklen_t *salenptr);

// 带打印客户端ip的accept
int Accept_with_print(int lfd);
#endif

猜你喜欢

转载自blog.csdn.net/qq_55796594/article/details/127686397