IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 第三-五章 TCP套接字及程序实例 -> 正文阅读

[网络协议]第三-五章 TCP套接字及程序实例

一、套接字编程简介

1.1 套接字地址结构

大多数套接字函数都需要一个指向套接字地址结构的指针作为参数。每个协议族都定义它自己的套接字地址结构。这些结构的名字均以**sockaddr_**开头,并以对应每个协议族的唯一后缀结尾。

  1. IPv4套接字地址结构
    sockaddr_in命名,定义在netinet/in.h中:
struct sockaddr_in{
	uint8_t 	   	sin_len; 	//length of structure
	sa_family_t 	sin_family; // AF_INET
	in_port_t 		sin_port;	//16-bit TCP or UDP port number
	struct in_addr  sin_addr;   //32-bit IPv4 address
	char 			sin_zero[8]; //unused
};

struct in_addr{
	in_addr_t		s_addr;
};

注:
(1)ip地址和端口号在套接字地址中总是以网络字节序来存储的
(2)32位ip地址存在两种不同的访问方法。sin_addr按in_addr结构体引用或sin_addr.s_addr按in_addr_t引用

  1. 通用套接字地址结构
    作为函数参数的套接字结构体指针,必须处理来自所支持的任何协议族的套接字地址结构,所以在如何声明所传递指针的数据类型上存在问题。(有了ANSI C后很简单,使用void*即可;然而套接字函数是在之前定义的)。采取的办法是在sys/socket.h头文件中定义一个通用的套接字地址结构
struct sockaddr{
	uint8_t		 sa_len;
	sa_family_t  sa_family;
	char 		 sa_data[14]; //14 = 2 + 4 + 8
};
  1. IPv6套接字地址结构
struct sockaddr_in6{
	uint8_t 	   	sin6_len; 	
	sa_family_t 	sin6_family;
	in_port_t 		sin6_port;	
	uint32_t 		sin6_flowinfo;
	struct in6_addr sin6_addr;  
	uint32_t 		sin6_scope_id;
};

struct in6_addr{
	uint8_t		    s6_addr[16];
};

1.2 字节排序函数

大端(big-endian):高字节存储在起始位置
小段(little-endian):低字节存储在起始位置

用联合体(关键代码)判断系统的主机字节序


union {
	short s;
	char  c[sizeof(short)];
}un;

un.s = 0x0102;

if(sizeof(short) == 2){
	if(un.c[0] == 1 && un.c[1] == 2)
		//大端
	else if(un.c[0] == 2 && un.c[1] == 1)
		//小端
	else
		//unkown
}

两种字节序之间的转换:

#include <netinet/in.h>

//主机到网络
uint16_t htons(uint16_t h16);
uint16_t htonl(uint32_t h32);

//网络到主机
uint16_t ntohs(uint16_t n16);
uint16_t ntohs(uint16_t n32);

1.3 字节操纵函数

名字以b开头(表示字节)的一组函数起源于4.2BSD,以mem开头(表示内存)的一组函数起源于ANSI C标准。

#include <strings.h>
void bzero(void* dest, size_t nbytes);
void bcopy(const void* src, void* dest, size_t nbytes);
int bcmp(const void* ptr1, const void* ptr2, size_t nbytes); //返回0则相等,否则不等
#include <string.h>
void* memset(void* dest, int c, size_t len);
void* memcpy(void* dest, void* src, size_t nbytes);
int memcmp(const void* ptr1, const void* ptr2, size_t nbytes); //返回0则相等,否则不等

不同之处:
所有的memXXX函数都需要一个长度参数,而且总是最后一个
(1)memset将指定数目的字节置为c,而bzero置为0
(2)memcpy与bcopy中指针参数顺序是相反的,记忆:dest=src,即赋值顺序
(3)memcpy比较字符串,非0时,返回值取决于第一个不等的字节。*ptr1>*ptr2(基于unsigned char比较),则大于0,否则小于0.

1.4 地址转换函数

两组转换函数:
(1)inet_aton、inet_addr(已舍弃)和inet_ntoa在点分十进制数串与它长度为32位的网络字节序二进制之间转换IPv4地址
(2)较新的inet_pton和inet_ntop对于IPv4地址和IPv6地址都适用。

#include <arpa/inet.h>
int inet_aton(const char* strptr, struct in_addr* addrptr); //字符串有效返回1,否则返回0
char* inet_ntoa(struct in_addr inaddr); // 返回一个指向点分十进制数串的指针

函数名中的p和n分别代表表达(presentation)和数值(numeric)。

#include <arpa/inet.h>
int inet_pton(int family, const char* strptr, void* addrptr);
//成功,返回1;输入不是有效的表达格式则为0,出错则为-1
const char* inet_ntop(int family, const void* addrptr, char* strptr, size_t len);
//成功,返回指向结果的指针,出错返回NULL

这两个函数的family函数即可以是AF_INET,也可以是AF_INET6。
inet_ntop将addrptr转换为strptr, len参数时目标存储单元的大小,以免该函数溢出其调用者的缓冲区。

1.5 自定义的函数readn、writen和readline

字节流套接字上的read和write函数所表现的行为不同于通常的文件I/O。字节流套接字上调用read和write输入或输出的字节数可能比请求的数量少,然而这不是出错的状态。通常是因为内核中套接字的缓冲区满,此时所需的是调用者再次调用read或write函数,以输入或输出剩余的字节。

为了避免让调用者来处理不足的字节计数值,我们使用xxxn函数:

#include "unp.h"
ssize_t readn(int fileds, void* buff, size_t nbytes);
ssize_t writen(int fileds, const void* buff, size_t nbytes);
ssize_t readline(int fileds, void* buff, size_t maxlen);
//均返回读或写的字节数,出错返回-1
#include "unp.h"
ssize_t readn(int fd, void* vptr, size_t n)
{
	size_t nleft;
	ssize_t nread;
	char* ptr = vptr;
	nleft = n;
	while(nleft > 0){
		if(nread = (read(fd, ptr, nleft)) < 0){
			if(errno == EINTR)
				nread = 0;  // call read() again
			else
				return -1;
		}else if(nread == 0)
			break;         // EOF
		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 = vptr;
	nleft = n;
	while(nleft > 0){
		if((nwritten = write(fd, ptr, nleft)) <= 0){
			if(nwritten < 0 && errno == EINTR)
				nwritten = 0;  // call write() again
			else
				return -1;    //error
		}
		nleft -= nwritten;
		ptr   += nwritten;
	}
	return n;
}

readn和writen的情况有所不同,read时,对端可能随时关闭套接字而读到EOF,此时未读到n字节数据;而writen在不出错的情况下,一定会写完全部数据, 返回n。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-11-10 12:45:58  更:2021-11-10 12:47:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 5:37:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码