1. 程式人生 > >libhttp服務端ipv6相容ipv4程式碼抽取重組

libhttp服務端ipv6相容ipv4程式碼抽取重組

簡單分析

https://github.com/lammertb/libhttp
c語言  WIN/UNIX
httplib_set_ports_option.c 執行緒:根據串創定[僅ipv4]或[僅ipv6]或[相容4_6]的服務端監聽,監聽套接字入監聽緩衝區
master_thread_run(void*thread_func_param)執行緒:監聽緩衝區中的有效socket輪詢poll檢視,並
XX_httplib_accept_new_connection,掩碼和屬性設定,新連線套接字入客戶端緩衝區
https://tools.ietf.org/html/rfc3513#section-2.2
so.sock = socket( so.lsa.sa.sa_family, SOCK_STREAM, 6 )
//the same port can be used again in the same process  /linux和WIN不一樣
 setsockopt( so.sock, SOL_SOCKET, SO_REUSEADDR, (SOCK_OPT_TYPE)&on, sizeof(on) ) != 0 
only  ipv4========
so.lsa.sa.sa_family == AF_INET
len = sizeof(so.lsa.sin);
bind( so.sock, &so.lsa.sa, len ) != 0
only  ipv6========
int    off         = 0;
so.lsa.sa.sa_family == AF_INET6
  setsockopt( so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&off, sizeof(off)
len = sizeof(so.lsa.sin6);
both  ipv4-ipv6====
 sizeof(so.lsa.sin6);
bind( so.sock, &so.lsa.sa, len ) != 0 )
----------------------------------------------------------
 listen( so.sock, SOMAXCONN )
getsockname( so.sock, &(usa.sa), &len ) != 0  ||  usa.sa.sa_family  !=  so.lsa.sa.sa_family
if ( so.lsa.sa.sa_family == AF_INET6 ) so.lsa.sin6.sin6_port = usa.sin6.sin6_port;
		else             so.lsa.sin.sin_port   = usa.sin.sin_port;
//
httplib_master_thread.c
select   accept--

Makefile編譯make

obj= c2.o   ipv4_6s.o  ci6.o
CC=gcc  
CFLAGS= -Wall -g
CPPFLAGS= -lpthread
.PHONY:th
th:$(obj)
$(obj):%.o:%.c
	$(CC)  $(CFLAGS) $(<) $(CPPFLAGS) -o $(@)
clean:
	rm *.o

執行

./ipv4_6.s.o
./c2.o
./ci6.o  ::1/128  8080

c2.c  ipv4 客戶端

#include<linux/sockios.h>
#include<sys/ioctl.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdio.h>
#include<errno.h>
void printfd(int sock)
{
	//thread  safe  C99
	socklen_t						peer_addr_size,selflen;
	struct sockaddr_in 			peer_addr,self;
	unsigned int				esme_port=0;
	char						esme_ip[200];
	int ern;
	int ret=0;
	peer_addr_size=sizeof(struct sockaddr);
	ret=getpeername(sock,(struct sockaddr*)&peer_addr,&peer_addr_size);
	ern=errno;
	if(!ret){
	printf("peer[%s]\n",inet_ntop(peer_addr.sin_family,&peer_addr.sin_addr,esme_ip,sizeof(esme_ip))); //ip  執行緒   inet_ntoa不安全  
	esme_port = ntohs(peer_addr.sin_port);
	printf("peer%d**[sock=%d] port[%d] ip[%s]\n",ret,sock,esme_port,esme_ip);
	}else
	{
		printf("peer %s\n",strerror(ern));
	}
	selflen=sizeof(struct sockaddr);
	ret=getsockname(sock, (struct sockaddr*)&self,&selflen);
	ern=errno;
	if(!ret){
	printf("self[%s]\n",inet_ntop(self.sin_family,&self.sin_addr,esme_ip,sizeof(esme_ip)));
	esme_port = ntohs(self.sin_port);
	printf("self%d**[sock=%d] port[%d] ip[%s]\n",ret,sock,esme_port,esme_ip);
	}else
	{
		printf("self %s\n",strerror(ern));
	}
}


int  main()
{
 
int sockClient;//客戶端Socket
struct sockaddr_in addrServer;//服務端地址

//新建客戶端socket
sockClient=socket(AF_INET,SOCK_STREAM,0);
if(sockClient<0)
{
	printf("socket errno\n");
	return 1;
}
//定義要連線的服務端地址
inet_pton(AF_INET, "127.0.0.1", &addrServer.sin_addr.s_addr);
//addrServer.sin_addr.s_addr=inet_addr("127.0.0.1");//目標IP(127.0.0.1是回送地址)
addrServer.sin_family=AF_INET;
addrServer.sin_port=htons(8080);//連線埠6000

//連線到服務端
if(connect(sockClient,(struct sockaddr*)&addrServer,sizeof(struct sockaddr))<0)
{
	printf("connect errno\n");
	return 1;
}

//傳送資料
char message[100]="HelloSocket!";
send(sockClient,message,strlen(message)+1,0);
 printfd(sockClient);
//接受伺服器返回,也可以用recv();並列印
int  bytes=0;
int ret;
sleep(1);
//ret=recv(sockClient,&bytes,sizeof(bytes),MSG_TRUNC );
//ret=ioctl(sockClient,SIOCINQ,&bytes);
//bytes=bytes>100?100:bytes;

//printf("ret%d Q-recv[%d]\n",ret,bytes);
//read(sockClient,message,bytes);
// printfd(sockClient);
//printf("%s",message);
 int sockfd=sockClient;


write(sockfd, "123", 3);  
    printf("wrote 3 bytes of normal data\n");  
    sleep(1);  
  
    send(sockfd, "4", 1, MSG_OOB);  
    printf("wrote 1 byte of OOB data\n");  
    sleep(1);  
  
    write(sockfd, "56", 2);  
    printf("wrote 2 bytes of normal data\n");  
    sleep(1);  
  
    send(sockfd, "mc7", 3, MSG_OOB);  
    printf("wrote 1 byte of OOB data\n");  
    sleep(1);  
  
    write(sockfd, "89", 2);  
    printf("wrote 2 bytes of normal data\n");  
    sleep(1);  
  
    exit(0);  



//關閉socket
close(sockClient);
return 0;}

ci6.c  ipv6客戶端

#include <stdio.h>  
#include <string.h>  
#include <errno.h>  
#include <sys/socket.h>  
#include <resolv.h>  
#include <stdlib.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#define MAXBUF 1024 
//原始碼來自: https://blog.csdn.net/hepeng597/article/details/7803277
//僅ipv6  客戶端
//ifconfig  inet6 看ipv6地址
//gcc ci6.c
//#執行  程式名  地址    埠
//run :./ci6.o  ::1/128  8080
//run :./ci6.o  fe40::20c:29ff:eec1:9896/64 8080 
int main(int argc, char **argv)  
{  
    int sockfd, len;  
    /* struct sockaddr_in dest; */ // IPv4  
    struct sockaddr_in6 dest;      // IPv6  
    char buffer[MAXBUF + 1];  
  
    if (argc != 3) {  
        printf  
            ("引數格式錯誤!正確用法如下:\n\t\t%s IP地址 埠\n\t比如:\t%s ::1/128 8080\n此程式用來從某個 IP 地址的伺服器某個埠接收最多 MAXBUF 個位元組的訊息",  
             argv[0], argv[0]);  
        exit(0);  
    }  
    /* 建立一個 socket 用於 tcp 通訊 */  
    /* if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { */ // IPv4  
    if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {      // IPv6  
        perror("Socket");  
        exit(errno);  
    }  
    printf("socket created\n");  
  
    /* 初始化伺服器端(對方)的地址和埠資訊 */  
    bzero(&dest, sizeof(dest));  
    /* dest.sin_family = AF_INET; */  // IPv4  
    dest.sin6_family = AF_INET6;     // IPv6  
    /* dest.sin_port = htons(atoi(argv[2])); */ // IPv4  
    dest.sin6_port = htons(atoi(argv[2]));     // IPv6  
    /* if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { */ // IPv4  
    if ( inet_pton(AF_INET6, argv[1], &dest.sin6_addr) < 0 ) {                 // IPv6  
        perror(argv[1]);  
        exit(errno);  
    }  
    printf("address created\n");  
  
    /* 連線伺服器 */  
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {  
        perror("Connect ");  
        exit(errno);  
    }  
    printf("server connected\n");  
  
    /* 接收對方發過來的訊息,最多接收 MAXBUF 個位元組 */  
    bzero(buffer, MAXBUF + 1);  
    /* 接收伺服器來的訊息 */  
    len = recv(sockfd, buffer, MAXBUF, 0);  
    if (len > 0)  
        printf("接收訊息成功:'%s',共%d個位元組的資料\n",  
               buffer, len);  
    else  
        printf  
            ("訊息接收失敗!錯誤程式碼是%d,錯誤資訊是'%s'\n",  
             errno, strerror(errno));  
  
    bzero(buffer, MAXBUF + 1);  
    strcpy(buffer, "這是客戶端發給伺服器端的訊息\n");  
    /* 發訊息給伺服器 */  
    len = send(sockfd, buffer, strlen(buffer), 0);  
    if (len < 0)  
        printf  
            ("訊息'%s'傳送失敗!錯誤程式碼是%d,錯誤資訊是'%s'\n",  
             buffer, errno, strerror(errno));  
    else  
        printf("訊息'%s'傳送成功,共傳送了%d個位元組!\n",  
               buffer, len);  
  
    /* 關閉連線 */  
    close(sockfd);  
    return 0;  
}  

ipv4_6s.c改造的服務端

#include<sys/socket.h>
#include<string.h>
#include<stdio.h>
#include <netdb.h>
#include <netinet/tcp.h>

#include<stdint.h>//uint64_t
#include <fcntl.h>
#include <poll.h>
#include<stdlib.h>//exit
#include<errno.h>
#include <stdbool.h>//bool
#include <unistd.h>//close
#include<malloc.h>
#include<pthread.h>

#if !defined(SOMAXCONN)
#define SOMAXCONN (100)
#endif

#define INVALID_SOCKET (-1)
#define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */
#define ERROR_STRING_LEN		(256)
#define ERRNO		errno
#define closesocket(fd)  close(fd)
#define IMGAPISTACKSIZE   10485760//10*1024*1024  執行緒棧  10MB

#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
//
#define httplib_cry( a, ctx, b, format,...)  printf(format,##__VA_ARGS__)  

typedef int SOCKET;
/* Unified socket address. For IPv6 support, add IPv6 address structure in the
 * union u. */
union usa {
	struct sockaddr sa;
	struct sockaddr_in sin;
	struct sockaddr_in6 sin6;
};
/* Describes listening socket, or socket which was accept()-ed by the master
 * thread and queued for future handling by the worker thread. */
struct socket {
	SOCKET sock;			// Listening socket					
	union usa lsa;			// Local socket address				
	union usa rsa;			//Remote socket address				
	//bool has_ssl;			Is port SSL-ed					
	//bool has_redir;			Is port supposed to redirect everything to SSL port	
	unsigned char in_use;		// Is valid	
	
};

typedef const void *SOCK_OPT_TYPE;

/* Describes a string (chunk of memory). */
struct vec {
	const char *	ptr;
	size_t		len;
};
struct ffcslh_ctx_t {
	struct socket *listening_sockets;//malloc  relloc  free
	struct pollfd *listening_socket_fds;//malloc  relloc  free
	unsigned int num_listening_sockets;
	char *	listening_ports;//eg.+8080  #服務端ip46相容   192.168.10.23:8080 #ipv4   [::1]:8080 #ipv6   192.168.10.23:6000,[::1]:8080 #多個 
};

//用一個size_t標記存的資料,因此a-1後申請空間
static int64_t				httplib_memory_blocks_used	= 0;
static int64_t				httplib_memory_bytes_used	= 0;
typedef void (*httplib_alloc_callback_func)( const char *file, unsigned line, const char *action, int64_t current_bytes, int64_t total_blocks, int64_t total_bytes );
static httplib_alloc_callback_func	alloc_log_func			= NULL;
#define					httplib_calloc(a, b) XX_httplib_calloc_ex(a, b, __FILE__, __LINE__)
#define					httplib_free(a) XX_httplib_free_ex(a, __FILE__, __LINE__)
#define					httplib_malloc(a) XX_httplib_malloc_ex(a, __FILE__, __LINE__)
#define					httplib_realloc(a, b) XX_httplib_realloc_ex(a, b, __FILE__, __LINE__)

void *			XX_httplib_calloc_ex( size_t count, size_t size, const char *file, unsigned line );
void *			XX_httplib_free_ex( void *memory, const char *file, unsigned line );
void *			XX_httplib_malloc_ex( size_t size, const char *file, unsigned line );
void *			XX_httplib_realloc_ex( void *memory, size_t newsize, const char *file, unsigned line );

void XX_httplib_close_all_listening_sockets( struct ffcslh_ctx_t *ctx ) {

	unsigned int i;

	if ( ctx == NULL ) return;

	for (i=0; i<ctx->num_listening_sockets; i++) {

		closesocket( ctx->listening_sockets[i].sock );
		ctx->listening_sockets[i].sock = INVALID_SOCKET;
	}

	ctx->listening_sockets    = httplib_free( ctx->listening_sockets    );
	ctx->listening_socket_fds = httplib_free( ctx->listening_socket_fds );

}  /* XX_close_all_listening_sockets */

void XX_httplib_set_close_on_exec( SOCKET fd ) { 
	fcntl( fd, F_SETFD, FD_CLOEXEC );
}  /* XX_httplib_set_close_on_exec */
int XX_httplib_inet_pton( int af, const char *src, void *dst, size_t dstlen ) {

	struct addrinfo hints;
	struct addrinfo *res;
	struct addrinfo *ressave;
	int func_ret;
	int gai_ret;

	func_ret = 0;

	memset( & hints, 0, sizeof(struct addrinfo) );
	hints.ai_family = af;

	gai_ret = getaddrinfo( src, NULL, &hints, &res );

	if ( gai_ret != 0 ) {

		/*
		 * gai_strerror could be used to convert gai_ret to a string
		 * POSIX return values: see
		 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html
		 *
		 * Windows return values: see
		 * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx
		 */

		return 0;
	}
	ressave = res;
	while ( res ) {

		if ( dstlen >= res->ai_addrlen ) {

			memcpy( dst, res->ai_addr, res->ai_addrlen );
			func_ret = 1;
		}
		res = res->ai_next;
	}
	freeaddrinfo( ressave );

	return func_ret;

}  /* XX_httplib_inet_pton */
int XX_httplib_is_valid_port( unsigned long port ) {

	return ( port < 0xffff );

}  
/*略過LWS 
 *,分隔
 *eg:
 *list:80, 443s, 127.0.0.1:3128, 192.0.2.3:8080s
 *list:[::]:80, [::1]:80, [2001:0db8:7654:3210:FEDC:BA98:7654:3210]:443s
 *list:+8080
 */
/* XX_httplib_is_valid_port */
const char *XX_httplib_next_option( const char *list, struct vec *val, struct vec *eq_val ) {

	int end;

reparse:
	if ( val == NULL  ||  list == NULL  ||  *list == '\0' ) return NULL;
	
	/*
	 * Skip over leading LWS
	 */

	while ( *list == ' '  ||  *list == '\t' ) list++;

	val->ptr = list;
	if ( (list = strchr( val->ptr, ',' )) != NULL ) {

		/*
		 * Comma found. Store length and shift the list ptr
		 */

		val->len = ((size_t)(list - val->ptr));
		list++;
	}
	
	else {

		/*
		 * This value is the last one
		 */

		list     = val->ptr + strlen(val->ptr);
		val->len = ((size_t)(list - val->ptr));
	}

	/*
	 * Adjust length for trailing LWS
	 */

	end = (int)val->len - 1;
	while ( end >= 0  &&  ( val->ptr[end] == ' ' || val->ptr[end] == '\t' ) ) end--;

	val->len = (size_t)(end + 1);

	if ( val->len == 0 ) goto reparse; /* Ignore any empty entries. */

	if ( eq_val != NULL ) {

		/*
		 * Value has form "x=y", adjust pointers and lengths
		 * so that val points to "x", and eq_val points to "y".
		 */

		eq_val->len = 0;
		eq_val->ptr = (const char *)memchr( val->ptr, '=', val->len );

		if ( eq_val->ptr != NULL ) {

			eq_val->ptr++; /* Skip over '=' character */
			eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len;
			val->len    = ((size_t)(eq_val->ptr - val->ptr)) - 1;
		}
	}

	return list;

}  /* XX_httplib_next_option */
char *httplib_error_string( int error_code, char *buf, size_t buf_len ) {

	if ( buf == NULL  ||  buf_len < 1 ) return NULL;
	strerror_r( error_code, buf, buf_len );
	return buf;

}  /* httplib_error_string */
static bool parse_port_string( const struct vec *vec, struct socket *so, int *ip_version ) {

	unsigned int a;
	unsigned int b;
	unsigned int c;
	unsigned int d;
	unsigned int port;
	int ch;
	int len;
	char buf[100] = {0};
	/*
	 * MacOS needs that. If we do not zero it, subsequent bind() will fail.
	 * Also, all-zeroes in the socket address means binding to all addresses
	 * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
	 */

	memset( so, 0, sizeof(*so) );
	so->lsa.sin.sin_family = AF_INET;
	*ip_version            = 0;
	if ( sscanf( vec->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len ) == 5 ) {

		/*
		 * Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
		 */

		so->lsa.sin.sin_addr.s_addr = htonl( (a << 24) | (b << 16) | (c << 8) | d );
		so->lsa.sin.sin_port        = htons( (uint16_t)port );

		*ip_version = 4;
	}
	else if ( sscanf( vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len ) == 2  &&  XX_httplib_inet_pton( AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6) ) ) {

		/*
		 * IPv6 address, examples: see above
		 * so->lsa.sin6.sin6_family = AF_INET6; already set by httplib_inet_pton
		 */

		so->lsa.sin6.sin6_port = htons( (uint16_t)port );
		*ip_version = 6;
	}
	else if ( vec->ptr[0] == '+'  &&  sscanf( vec->ptr + 1, "%u%n", &port, &len ) == 1 ) {

		/*
		 * Port is specified with a +, bind to IPv6 and IPv4, INADDR_ANY
		 * Add 1 to len for the + character we skipped before
		 */

		len++;

		/*
		 * Set socket family to IPv6, do not use IPV6_V6ONLY
		 */
		so->lsa.sin6.sin6_family = AF_INET6;
		so->lsa.sin6.sin6_port   = htons(( uint16_t)port );

		*ip_version = 4 + 6;
	}
	else if ( sscanf( vec->ptr, "%u%n", &port, &len ) == 1 ) {

		/*
		 * If only port is specified, bind to IPv4, INADDR_ANY
		 */

		so->lsa.sin.sin_port = htons( (uint16_t)port );
		*ip_version = 4;

	}
	else {
		/*
		 * Parsing failure. Make port invalid.
		 */

		port = 0;
		len  = 0;
	}
	/*
	 * sscanf and the option splitting code ensure the following condition
	 */

	if ( len < 0 && ((unsigned)len) > ((unsigned)vec->len) ) {

		*ip_version = 0;
		return false;
	}

	ch            = vec->ptr[len]; /* Next character after the port number */
	//so->has_ssl   = ( ch == 's' );
	//so->has_redir = ( ch == 'r' );

	/*
	 * Make sure the port is valid and vector ends with 's', 'r' or ','
	 */

	if ( XX_httplib_is_valid_port( port )  &&  ( ch == '\0'  ||  ch == 's'  ||  ch == 'r'  ||  ch == ',' ) ) return true;

	/*
	 * Reset ip_version to 0 if there is an error
	 */

	*ip_version = 0;
	return false;

}  /* parse_port_string */

int FFCSXX_httplib_set_ports_option( struct ffcslh_ctx_t *ctx ) {

	const char *list;
	char error_string[ERROR_STRING_LEN];
	int on;
	int off;
	struct vec vec;
	struct socket so;
	struct socket *ptr;
	struct pollfd *pfd;
	union usa usa;
	socklen_t len;
	int ip_version;
	int ports_total;
	int ports_ok;

	if ( ctx == NULL ) return 0;

	on          = 1;
	off         = 0;
	ports_total = 0;
	ports_ok    = 0;

	memset( & so,  0, sizeof(so)  );
	memset( & usa, 0, sizeof(usa) );

	len  = sizeof(usa);
	list = ctx->listening_ports;

	while ( (list = XX_httplib_next_option( list, &vec, NULL )) != NULL ) {

		ports_total++;

		if ( ! parse_port_string( &vec, &so, &ip_version ) ) {

			httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: %.*s: invalid port spec (entry %i). Expecting list of: %s", __func__, (int)vec.len, vec.ptr, ports_total, "[IP_ADDRESS:]PORT[s|r]" );
			continue;
		}
		if ( ( so.sock = socket( so.lsa.sa.sa_family, SOCK_STREAM, 6 ) ) == INVALID_SOCKET ) {

			httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: cannot create socket (entry %i)", __func__, ports_total );
			continue;
		}

		if ( setsockopt( so.sock, SOL_SOCKET, SO_REUSEADDR, (SOCK_OPT_TYPE)&on, sizeof(on) ) != 0 ) {

			/*
			 * Set reuse option, but don't abort on errors.
			 */

			httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: cannot set socket option SO_REUSEADDR (entry %i)", __func__, ports_total );
		}

		if ( ip_version > 4 ) {

			if ( ip_version == 6 ) {

				if ( so.lsa.sa.sa_family == AF_INET6  &&  setsockopt( so.sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&off, sizeof(off) ) != 0 ) {

					/*
					 * Set IPv6 only option, but don't abort on errors.
					 */

					httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: cannot set socket option IPV6_V6ONLY (entry %i)", __func__, ports_total );
				}
			}
		}

		if ( so.lsa.sa.sa_family == AF_INET ) {

			len = sizeof(so.lsa.sin);

			if ( bind( so.sock, &so.lsa.sa, len ) != 0 ) {

				httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: cannot bind to %.*s: %d (%s)", __func__, (int)vec.len, vec.ptr, (int)ERRNO, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
				closesocket( so.sock );
				so.sock = INVALID_SOCKET;
				continue;
			}
		}

		else if ( so.lsa.sa.sa_family == AF_INET6 ) {

			len = sizeof(so.lsa.sin6);

			if ( bind( so.sock, &so.lsa.sa, len ) != 0 ) {

				httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: cannot bind to IPv6 %.*s: %d (%s)", __func__, (int)vec.len, vec.ptr, (int)ERRNO, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
				closesocket( so.sock );
				so.sock = INVALID_SOCKET;
				continue;
			}
		}

		else {
			httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: cannot bind: address family not supported (entry %i)", __func__, ports_total );
			continue;
		}

		if ( listen( so.sock, SOMAXCONN ) != 0 ) {

			httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: cannot listen to %.*s: %d (%s)", __func__, (int)vec.len, vec.ptr, (int)ERRNO, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
			closesocket( so.sock );
			so.sock = INVALID_SOCKET;
			continue;
		}

		if ( getsockname( so.sock, &(usa.sa), &len ) != 0  ||  usa.sa.sa_family  !=  so.lsa.sa.sa_family ) {

			int err = (int)ERRNO;
			httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: call to getsockname failed %.*s: %d (%s)", __func__, (int)vec.len, vec.ptr, err, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
			closesocket( so.sock );
			so.sock = INVALID_SOCKET;
			continue;
		}

/*
 * Update lsa port in case of random free ports
 */

		if ( so.lsa.sa.sa_family == AF_INET6 ) so.lsa.sin6.sin6_port = usa.sin6.sin6_port;
		else                                   so.lsa.sin.sin_port   = usa.sin.sin_port;

		ptr = httplib_realloc( ctx->listening_sockets, (ctx->num_listening_sockets+1) * sizeof(ctx->listening_sockets[0]) );

		if ( ptr != NULL ) ctx->listening_sockets = ptr;
		else {
			httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: out of memory on listening sockets", __func__ );
			closesocket( so.sock );
			so.sock = INVALID_SOCKET;
			continue;
		}


		pfd = httplib_realloc( ctx->listening_socket_fds, (ctx->num_listening_sockets+1) * sizeof(ctx->listening_socket_fds[0]) );

		if ( pfd != NULL ) ctx->listening_socket_fds = pfd;
		else {
			httplib_cry( LH_DEBUG_CRASH, ctx, NULL, "%s: out of memory on fds", __func__ );
			closesocket( so.sock );
			so.sock = INVALID_SOCKET;
			continue;
		}


		XX_httplib_set_close_on_exec( so.sock );

		ctx->listening_sockets[ctx->num_listening_sockets] = so;
		ctx->num_listening_sockets++;

		ports_ok++;
	}

	if ( ports_ok != ports_total ) {

		XX_httplib_close_all_listening_sockets( ctx );
		ports_ok = 0;
	}

	return ports_ok;

}  /* XX_httplib_set_ports_option */


 void *XX_httplib_realloc_ex( void *memory, size_t newsize, const char *file, unsigned line ) {

	size_t *olddata;
	size_t *newdata;
	size_t oldsize;
	int64_t diff;

	if ( newsize == 0 ) {

		if ( memory != NULL ) XX_httplib_free_ex( memory, file, line );
		return NULL;
	}

	if ( memory == NULL ) return XX_httplib_malloc_ex( newsize, file, line );

	olddata = ((size_t *)memory) - 1;
	oldsize = *olddata;
	newdata = realloc( olddata, newsize + sizeof(size_t) );
	if ( newdata == NULL ) {
		
		if ( alloc_log_func != NULL ) alloc_log_func( file, line, "realloc", 0, httplib_memory_blocks_used, httplib_memory_bytes_used );
		return NULL;
	}

	httplib_memory_bytes_used -= oldsize;
	httplib_memory_bytes_used += newsize;

	*newdata = newsize;
	diff     = ((int64_t)newsize) - ((int64_t)oldsize);

	if ( alloc_log_func != NULL ) alloc_log_func( file, line, "realloc", diff, httplib_memory_blocks_used, httplib_memory_bytes_used );

	return (newdata+1);

}  /* ffcsXX_httplib_realloc_ex */
void *XX_httplib_malloc_ex( size_t size, const char *file, unsigned line ) {

	size_t *data;

	if ( size == 0 ) {

		if ( alloc_log_func != NULL ) alloc_log_func( file, line, "malloc", 0, httplib_memory_blocks_used, httplib_memory_bytes_used );
		return NULL;
	}

	data = malloc( size + sizeof(size_t) );

	if ( data == NULL ) {
	
		if ( alloc_log_func != NULL ) alloc_log_func( file, line, "malloc", 0, httplib_memory_blocks_used, httplib_memory_bytes_used );
		return NULL;
	}

	httplib_memory_bytes_used += size;
	httplib_memory_blocks_used++;

	*data = size;

	if ( alloc_log_func != NULL ) alloc_log_func( file, line, "malloc", size, httplib_memory_blocks_used, httplib_memory_bytes_used );

	return (data+1);

}  /* XX_httplib_malloc_ex */
void *XX_httplib_free_ex( void *memory, const char *file, unsigned line ) {

	size_t *data;

	if ( memory == NULL ) return NULL;

	data = ((size_t *)memory) - 1;

	httplib_memory_bytes_used -= *data;
	httplib_memory_blocks_used--;

	if ( alloc_log_func != NULL ) alloc_log_func( file, line, "free", - ((int64_t)*data), httplib_memory_blocks_used, httplib_memory_bytes_used );

	free( data );

	return NULL;

}  /* XX_httplib_free_ex */
void *XX_httplib_calloc_ex( size_t count, size_t size, const char *file, unsigned line ) {

	void *data;

	data = XX_httplib_malloc_ex( size*count, file, line );
	if ( data == NULL ) return NULL;

	memset( data, 0x00, size*count );

	return data;

}  /* XX_httplib_calloc_ex */


int httplib_poll( struct pollfd *pfd, unsigned int n, int milliseconds ) 
{
		return poll( pfd, n, milliseconds );
}


void XX_httplib_sockaddr_to_string( char *buf, size_t len, const union usa *usa ) {

	if ( usa == NULL  ||  buf == NULL  ||  len < 1 ) return;

	buf[0] = '\0';

	if      ( usa->sa.sa_family == AF_INET  ) getnameinfo(&usa->sa, sizeof(usa->sin),  buf, (unsigned)len, NULL, 0, NI_NUMERICHOST );
	else if ( usa->sa.sa_family == AF_INET6 ) getnameinfo(&usa->sa, sizeof(usa->sin6), buf, (unsigned)len, NULL, 0, NI_NUMERICHOST );

}  /* XX_httplib_sockaddr_to_string */

void*PostRoutine(void*pram) 
{//Linux
	struct socket *psock=(struct socket *)pram;
	int connfd=psock->sock;
	int ret;
	free(pram);
	psock=pram=NULL;
	write(connfd,"you link xjylibhttp ok !",24);
     char buf[1024];
    fd_set read_fds;
    fd_set exception_fds;
    FD_ZERO(&read_fds);
    FD_ZERO(&exception_fds);

    while(1)
    {
        memset(buf, '\0', sizeof(buf));
        /* 每次呼叫select前都要重新在read_fds和exception_fds中設定檔案描述符connfd,因為事件發生之後,檔案描述符集合將被核心修改 */
        FD_SET(connfd, &read_fds);
        FD_SET(connfd, &exception_fds);
        ret = select(connfd + 1, &read_fds, NULL, &exception_fds, NULL);
        if(ret < 0)
        {
            printf("selection failure\n");
            break;
        }

        /* 對於可讀事件,採用普通的recv函式讀取資料 */
        if(FD_ISSET(connfd, &read_fds))
        {
            ret = recv(connfd, buf, sizeof(buf) - 1, 0);
            if(ret <= 0)
            {
                break;
            }
            printf("get %d bytes of normal data: %s\n", ret, buf);
        }
        /* 對於異常事件,採用帶MSG_OOB標誌的recv函式讀取帶外資料 */
        else if(FD_ISSET(connfd, &exception_fds))
        {
            ret = recv(connfd, buf, sizeof(buf) - 1, MSG_OOB);
            if(ret <= 0)
            {
                break;
            }
            printf("get %d bytes of oob data: %s\n", ret, buf);
        }
    }
    close(connfd);
	pthread_exit(NULL);
}
int pthreadFormPost(void* psock) 
{//Linux
	int ret = 0;
	int eno;
	pthread_attr_t  attr; 
	pthread_t  pthreadid;
	struct socket *pram=NULL;
	ret=pthread_attr_init(&attr);
	if (0 != ret) 
	{
		printf("pthread_attr_init()error\n");
		return ret;
	}
	ret = pthread_attr_setstacksize(&attr, IMGAPISTACKSIZE);
	if (0 != ret)
	{
		printf("pthread_attr_setstacksize()error\n");
		return ret;
	}
	ret= pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//ChildThread quit and free source
	if (0 != ret)
	{
		printf("pthread_attr_setdetachstate()error\n");
		return ret;
	}
	pram=malloc(sizeof(struct socket));
	if(NULL==pram)
	{
		printf("pthread malloc()error\n");
		return -1;
	}
	
    memcpy(pram,psock,sizeof(struct socket));
	ret = pthread_create(&pthreadid,&attr,PostRoutine,pram);
	eno = errno;
	if (0 != ret) 
	{
		free(pram);
		printf("pthread_create() errno:%d", eno); 
		return ret;
	}
	pthread_attr_destroy(&attr);
	return 0;
}

void FFCSXX_httplib_accept_new_connection( const struct socket *listener, struct ffcslh_ctx_t*ctx ) {

	struct socket so;
	//char src_addr[IP_ADDR_STR_LEN];
	char error_string[ERROR_STRING_LEN];
	socklen_t len;
	int on;

	if ( listener == NULL ) return;

	on  = 1;
	len = sizeof(so.rsa);

	so.sock = accept( listener->sock, &so.rsa.sa, &len );
	if ( so.sock == INVALID_SOCKET ) return;
		/*
		 * Put so socket structure into the queue
		 */
	//原來碼此處 檢驗IP是否符合掩碼要求
		XX_httplib_set_close_on_exec( so.sock );


		if ( getsockname( so.sock, &so.lsa.sa, &len ) != 0 ) {

			httplib_cry( LH_DEBUG_ERROR, ctx, NULL, "%s: getsockname() failed: %s", __func__, httplib_error_string( ERRNO, error_string, ERROR_STRING_LEN ) );
		}

	//原始碼此處 開啟TCP socket的 保活、 TCP Nagle's algorithm、收發超時設定 --此處刪掉
	if(pthreadFormPost(&so)!=0)
	printf("pthreadFormPost error\n");

}  /* XX_httplib_accept_new_connection */
 void FFcsmaster_thread_run(void*thread_func_param) 
{
	int i;
	struct ffcslh_ctx_t*ctx = (struct ffcslh_ctx_t*)thread_func_param;
	struct pollfd *pfd;
		pfd = ctx->listening_socket_fds;

	while (1) {

		for (i=0; i<(int)ctx->num_listening_sockets; i++) {

			pfd[i].fd     = ctx->listening_sockets[i].sock;
			pfd[i].events = POLLIN;
		}

		if ( httplib_poll( pfd, ctx->num_listening_sockets, 200 ) > 0 ) {

			for (i=0; i<(int)ctx->num_listening_sockets; i++) {

				/*
				 * NOTE(lsm): on QNX, poll() returns POLLRDNORM after the
				 * successful poll, and POLLIN is defined as
				 * (POLLRDNORM | POLLRDBAND)
				 * Therefore, we're checking pfd[i].revents & POLLIN, not
				 * pfd[i].revents == POLLIN.
				 */

				if ((pfd[i].revents & POLLIN)) 
				{	
					FFCSXX_httplib_accept_new_connection( & ctx->listening_sockets[i], ctx ); 
				}
			}
		}
	}
	
}
int  main()
{
	struct ffcslh_ctx_t ctx;
	memset(&ctx,0,sizeof(struct ffcslh_ctx_t));
	ctx.listening_ports="+8080";//sever  ipv4+ipv6  可監聽多種埠
	  if(0==FFCSXX_httplib_set_ports_option(&ctx))//socket-bind->listen   
	  {
		  printf("FFCSXX_httplib_set_ports_option error\n");
		  exit(1);
	  }
	  
      FFcsmaster_thread_run(&ctx);//poll --accept--
	 
	  /*
	 * Stop signal received: somebody called httplib_stop. Quit.
	 */
	XX_httplib_close_all_listening_sockets( &ctx );
		return 0;
}