Unix network programming (1) Encapsulation of commonly used socket functions

The process of socket connection on server side: create socket ====> bind ip and port ====> monitor listen ====> accept connection accept

Since the above process is general, there are still a lot of misjudgments and network byte order conversions in the process, so it is encapsulated into some functions for easy calling.

Among them, accpet is susceptible to signal interruption and software interruption, use the goto statement, and accept again when interrupted.

Port multiplexing:

It is unreasonable to not allow re-listening until the server's TCP connection is completely disconnected. Because the TCP connection is not completely disconnected, it means that connfd (127.0.0.1:6666) is not completely disconnected, and we are listening to lis-tenfd (0.0.0.0:6666), although it occupies the same port, but the IP The addresses are different, connfd corresponds to a specific IP address communicating with a certain client, and listenfd corresponds to the wildcard address. The solution to this problem is to use setsockopt() to set the SO_REUSEADDR option of the socket descriptor to 1, which means that multiple socket descriptors with the same port number but different IP addresses are allowed to be created.

Insert the following code between the socket() and bind() calls in the server code:

int opt = 1;

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

pub.c file 

// 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 file

// 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

Guess you like

Origin blog.csdn.net/qq_55796594/article/details/127686397