Unix网络编程 卷1 第3章:套接字编程简介(套接字地址结构)

通常大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。每个协议族都定义了他们各自的套接字地址结构。这些地址结构以sockaddr_开头,并对应各自唯一的后缀。

一、IPv4套接字地址结构

IPv4套接字地址结构通常称为“网际套接字地址结构”,它以sockadd_in命名。头文件及声明如下:

#include <netinet/in.h> 

struct sockaddr_in{
        uint8_t         sin_len;
        sa_family_t     sin_family;  /*AF_INET*/
        in_port_t       sin_port;    /*16位TCP或者UDP端口号,网络字节序*/
        struct in_addr  sin_addr;    /*32位IPv4结构,网络字节序*/
        char            sin_zero[8]; /*未使用*/
};

struct in_addr{
        in_addr_t s_addr;     /*32位IPv4地址,网络字节序*/
};

其中,数据类型如下图所示:


二、IPv6套接字地址结构

IPv6套接字地址结构头文件及结构体内容如下:

#include <sys/socket.h>

#define SIN6_LEN                         // required for compile-time tests
struct sockaddr_in6
{
    uint8_t int       sin6_len;   //IPv6结构长度,是一个无符号的8为整数,表示128为IPv6地址长度	
	sa_family_t       sin6_family; //地址类型AF_INET6
	int_port_t        sin6_port;   //存储端口号,按网络字节顺序

	uint32_t          sin6_flowinfo;  //低24位是流量标号,然后是4位的优先级标志,剩下四位保留
	struct in6_addr   sin6_addr;      //IPv6地址,网络字节顺序
};

struct in6_addr
{
     	uint8_t s6_addr;  //128位的IPv6地址,网络字节顺序
};

三、通用套接字结构

为什么需要通用套接字地址结构?

当作为一个参数传递给任何套接字函数时,套接字地址结构总是以引用形式(也就是以指向该结构的指针)来传递。然而以这样的指针作为参数之一的任何套接字函数必须处理来自所支持的任何协议族套接字地址结构。因此,我们需要强制转换特定协议的套接字地址结构为通用的套接字地址结构,然后再传递给套接字函数。通用套接字地址结构体及头文件如下:

#include <sys/socket.h>

struct sockaddr{
        uint8_t       sa_len;
        sa_family_t   sa_family;   /*地址族:AF_xxx*/
        char          sa_data[4];  /*protocol-specific address*/
};

请记住:设计通用套接字地址结构的目的是把它作为参数传递给套接字函数。唯一用途是对指向特定协议(IPv4/   IPv6<支持吗?>)的套接字地址结构的指针进行强制类型转换,转换为通用套接字地址结构的指针,再传递给套接字函数。

比如:套接字函数bind原型为: int bind(int sockfd, const struct sockaddr *addr, socklen_taddrlen); 我们不能为第二个参数直接传递指向特定协议的套接字地址结构的指针,需要先转换为通用套接字地址结构的指针。正确的使用方法如下:

struct sockaddr_in serv; // IPv4套接字地址结构

bind(sockfd, (struct sockaddr*) &serv, sizeof(serv));

四、新的通用套接字地址结构

不像struct sockaddr,在IPv6里提出的新的struct sockaddr_storage足以容纳系统所支持的任何套接字地址结构(包括IPv4, IPv6),sockaddr_storage结构在<netinet/in.h>头文件中定义。

struct sockaddr_storage {
  	uint8_t   ss_len;   /* length of this struct (implementation dependent) */
  	sa_family_t  ss_family;    /* address family: AF_xxx value */

  	/* implementation-dependent elements to provide:
   	* a) alignment sufficient to fulfill the alignment requirements of
   	*    all socket address types that the system supports.
   	* b) enough storage to hold any type of socket address that the
   	*    system supports.
   	*/
};

sockaddr_storage和sockaddr的主要差别

(1) sockaddr_storage通用套接字地址结构满足对齐要求。 

(2) sockaddr_storage通用套接字地址结构足够大,能够容纳系统支持的任何套接字地址结构。

五、用tcp_connect重写的时间获取客户程序(参见11.12节)

下面的程序使用了tcp_connect重写时间获取客户程序,稍微修改了11.12节代码以考查了如何从sockaddr_storage结构中获取IP地址。因为sockaddr_storage结构比较复杂,而且兼容了IPv4和IPv6地址结构,无法直接从中取得IP地址或端口号,那么如何取得IP地址呢?请细读第42行代码。

#include <unistd.h>
#include <sys/types.h>       /* basic system data types */
#include <sys/socket.h>      /* basic socket definitions */
#include <netinet/in.h>      /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h>       /* inet(3) functions */
#include <sys/select.h>      /* select function*/
#include <netdb.h>           

#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

#define MAXLINE 4098

// use getaddrinfo
int tcp_connect(const char *host, const char *serv);

int main(int argc, char **argv)
{
	int sockfd, nread;
	struct sockaddr_storage ss;
	in_addr_t sinaddr;

	socklen_t addrlen;
	char recvBuf[MAXLINE + 1];

	char dst[MAXLINE];

	if(argc != 3)
	{
		perror("usage: daytimetcpcli <hostname/IPaddress> <service/port#>");
	}

	// tcp_connect calling
	sockfd = tcp_connect(argv[1], argv[2]);

	addrlen = sizeof(ss);	
	getpeername(sockfd, (struct sockaddr*)&ss, &addrlen);
	//printf("Connected to %s\n", sock_ntop_host((struct sockaddr*)&ss, addrlen) );

	sinaddr = ((struct sockaddr_in*)((struct sockaddr*)&ss))->sin_addr.s_addr;		

	printf("Connected to:%s \n",  inet_ntop(AF_INET, &sinaddr, dst ,addrlen ));
	

	while((nread = read(sockfd, recvBuf, MAXLINE)) > 0){
		recvBuf[nread] = 0;// null terminate
		fputs(recvBuf, stdout);
	}
}

int tcp_connect(const char *host, const char *serv)
{
	int    sockfd, n;
        struct addrinfo hints, *res, *ressave;

        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0){
        	printf("tcp_connect error for %s, %s: %s",
                                 host, serv, gai_strerror(n));	
	}
        ressave = res;

        do {
                sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
                if (sockfd < 0)
                        continue;       /* ignore this one */

                if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
                        break;          /* success */

                close(sockfd);  /* ignore this one */
        } while ( (res = res->ai_next) != NULL);

        if (res == NULL){        /* errno set from final connect() */
        	printf("tcp_connect error for %s, %s", host, serv);
	}
        freeaddrinfo(ressave);

        return(sockfd);
}
调试及结果如下图:





基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip基于bert实现关系三元组抽取python源码+数据集+项目说明.zip 个人大四的毕业设计、课程设计、作业、经导师指导并认可通过的高分设计项目,评审平均分达96.5分。主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 [资源说明] 不懂运行,下载完可以私聊问,可远程教学 该资源内项目源码是个人的毕设或者课设、作业,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96.5分,放心下载使用! 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),供学习参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值