最后更新:2021-12-03 06:59:29 手机定位技术交流文章
网络层面的“ IP 地址” 可以识别网络中唯一的主机, 而传输层面的“ 端口” 可以识别主机中的应用程序( 程序) 。 这样, 网络的进程就可以使用三重( IP 地址、 协议、 端口) 和网络中的流程通信与其他进程进行互动。 使用 TCP/ IP 协议的应用程序通常使用应用程序编程接口: UNIX BSD 的 Socket ( socket) 实现网络进程之间的通信。 目前, 几乎所有应用程序都以套接头形式出现, 套接头已经是网络的时代, 流程无处不在, 这就是为什么我说“ 全部都是套接字 ” 。 TCP/ IP 部落包括传输层、 网络层、 链层, 而 Socket 则是与TCP/ IP 部落沟通的中间软件的抽象层。
Socket 起源于 Unix, 其中一个关键的 Unix/ Linux 哲学是“ 所有文件 ” 。 在许多操作系统中, API 最初是作为 UNIX 操作系统的一部分建立的, 所以 API 与系统的其他 I/ O 设备融合。 当一个应用程序要创建互联网通信套接字( socket) 时, 操作系统返回一个小整数, 作为字符串标记的描述符 。 程序然后使用描述符作为传输参数参数 。
在现实生活中,当他听到钟声时,一个拨号和B通电话,所以A和B连通和对讲。讨论结束后,电话挂断,对话结束。电话对话以简单的措辞概括了操作理念:“开放/读近”方法。这是网络口盘互动的基本流程。
socket函数
插入套接字( 网格, 内型, 内型, 内型协议); 参数: 域 : 指定的通信传输域 : AF_ UNIX: 本地主机通信, 与 IPC 可比: AF_ INET: 互联网地址 IPV4 协议 AF_ INET6: 互联网地址 IPV6 协议类型: 指定值 : SOCK_ Stream (流量)、 SOCK_ DGRAM ( 数据电文)、 SOCK_ RAW ( 原始线索)、 协议 : TCP, UDP, 通常为 0, 自动选择与类型对应的协议类型:1 补充: SOCK_ Stream (简讯) 无法访问 TPCP 协议, 它提供可靠的序列, 以与 KOKwoknet 固定集( 互联网协议的第一版) 的双向链接为基础 。
connect函数
TCP 客户端在调用套接字以通过调用连接功能构建 socket_fd 后连接到服务器。 如果客户端调用连接函数以传输连接请求, 服务器将接收请求并返回接受功能。 接受功能返回的新文件描述符与客户端的 TCP 连接相对应, 客户端与服务器端之间的通信可以通过两个文件描述符( 客户连接fd 和服务器端接受返回 fd ) 进行 。
结合( 锁定、 对比度、 索卡迪尔* addr、 socklen_ t addrlen; 参数 sockfd: 客户端的套接字() 生成的描述性附加器: 服务器的套接字地址信息, 其中面包含有服务器IP 地址和端口地址等信息
您必须先配置服务器的 IP 地址、 端口信息、 和 Attr 之后才能启用连接功能 。
//地址清零,ipv4,把端口转换网络字节序,点十进制转发为32为整形ip地址
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT);
inet_aton(SERVER_IP,&serv_addr.sin_addr);
rv = connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
if(rv < 0)
{
printf("Failed to connect server:%sn",SERVER_IP,SERVER_PORT,strerror(errno));
return -2;
}
printf("Connecting to the server[%s:%d] successfullyn",SERVER_IP,SERVER_PORT);
bind函数
当调用以创建套接字时, 返回的套接字描述是在协议家庭空间中, 但是没有具体地址, 如果您想要给它一个地址, 您必须调用绑定功能 。 通常, 服务器会以一个众所周知的地址( IP 地址+端口号) 激活, 客户可以连接服务器; 客户不需要指定, 系统会自动指定一个端口号码和自己的 ip 地址组合 。 这就是为什么服务器结束通常在监听功能之前调用绑定函数, 客户不会调用, 而是在连接功能中随机由系统创建 。 当然, 客户也可以在调用连接功能之前访问一个绑定地址和端口, 以便使用特定的 IP 和端口连接服务器 。
绑定( in sockfd, const rules sockaddr*addr, socklen_t addrlen); 参数 sockfd: 由套接字() 方法生成的套接字描述是唯一识别套接字的。 捆绑() 函数用于给此描述一个名称 。 Addr: a Const 构造的套接字* 指针到协议地址, 以便与套接结。 此地址结构变化取决于地址组在生成地址时的地址组, 但最终需要将转换值传输到套接字机类型到内核心 Adrlen : ddr 的等长 。
在使用绑定方法之前,您必须另外配置客户端的 IP 地址、 端口信息、 添加器 。
//清零地址,ipv4, 端口网络转换主机字节序,监听所以的ip转换为主机字节序
memset(&serv_addr,0 ,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(socket_fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))<0)
{
printf("create socket failure:%sn",strerror(errno));
return -2;
}
printf("socket[%d] bind on port [%d] for all ip address okn",socket_fd,LISTEN_PORT);
通用 Socketsockddr 类型定义
typedef 未签名短短的 sa_ family_t; 规则 lockadr sa_ family_ t sa_ family; / *2 字节地址, AF_xx*/char sa_ data[14]; / *14 字节地址, AF_xx*/char sa_data[14];
IPv4 套接字套接字 sockaddr_ in type type 定义
AF_xx*/in_port_tsin_port; * * * */在_addr中支架; / *4 byte IPv4 */未签名数据 */未签名字符sin_0[8]; / *8字节未使用,且始终为零 */
IPv6 socket sockaddr_ in6 类型定义
typedef unsigned short sa_family_t;
typedef uint16_t in_port_t;
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
} __in6_u;
}
struct sockaddr_in6 {
sa_family_t sin6_family; /*2B*/
in_port_t sin6_port; /*2B*/
uint32_t sin6_flowinfo; /*4B*/
struct in6_addr sin6_addr; /*16B*/
uint32_t sin6_scope_id; /*4B*/
};
Unix 域域 Sockaddr_un type 描述
#define UNIX_PATH_MAX 108
struct sockaddr_un
{
sa_family_t sun_family;
char sun_path[UNIX_PATH_MAX];
};
listen函数
Int 听 (in lockfd, intbacklog) ; 参数 sockefd: sockefd: sockefd: sockefd: sockefd () 返回在内部核心中可以排队的最大连接数量, 以便构建用于收听积压的套接字配置 :
accept函数
在 TCP 服务器终端调用套接字功能、 绑定功能和监听功能之后, 提供套接字地址会被窃听。 服务器然后调用接收功能接收客户端的连接请求, 默认情况下, 即是一个阻塞功能, 这意味着程序会一直被屏蔽, 直到客户端使用连接功能。 在客户端调用连接功能后, 服务器会接受函数返回, 整个 TCP 链接会形成 。
Int 接受 (int sockfd, binish shockddr *ddr, socklen_t *ddrlen); 参数 sockfd: 服务器开始使用套接字() 功能, 称为监听套接字描述器; adder: 用于返回客户端的协议地址, 其中包括客户端提供的IP 和端口信息; addrlen: 返回客户端协议地址的长度 ;
例如代码
//监听socket,等待客户端连接,创建一个新的fd,创建新的客户端地址和客户端地址长度
client_fd = accept(socket_fd,(struct sockaddr*)&cli_addr,&cliaddr_len);
if(client_fd <0)
{
printf("accept new socket failure :%sn",strerror(errno));
return -2;
}
//将网络地址转换点十进制地址,把客户端地址的ipv4和客户端端口转发成点十进制
printf("accept new client[%s:%d] wlth fd [%d]n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),client_fd);
接受功能的返回值是由内核自动生成的全新的描述符(fd), 它代表着与返回的客户端 TCP 的连接。 如果您想要将数据发送到客户端, 我们可以调用一个函数, 如写( ) 写到客户端, 如果您想要读取客户端的内容, 请调用一个函数, 如读取 fd.A 服务器的数据, 通常只创建一个监听套接字描述, 它在整个服务器生命周期中继续存在 。 内核为服务器所接受的每个客户端连接创建一个新的套接字描述符。 当服务器完成对客户的服务时, 客户的对应套接字描述符应该关闭 。
函数 htons/ htonl
它意味着从主机字节顺序(小字节顺序)转换为网络字节顺序(大字节顺序)。
最常见的有两种
小内衣: 将小字节保留在起始地址中 。
大内衣:在起始地址保留高阶字节。
htons: 简短的主机网络(2 字节-16 位), 端口号( 16 位) 。
htonl:长期主机网络(4x32个地点),32 IP地址
viint32_t htonl (inint32_t htonl) ; viint16_t htons (int16_t hosthort); vient16_t htons (int32_t netlong); vient16_t t t htonl 将端口端端口转换为网络字节; htons 将主端端口的序列转换为网络字节; Nethl 将未签名的端口端口端口端口转换为主端口端口; intohs 将把未签名的端口端口端口端口端口端口端口端口端口端口号移到主机端口端口端口号; NADR_ANY_ANY_addr@DIR@DR.serv_DR.
例如,考虑一个知识产权地址。
由Flickr用户pic.twitter.com 1 将IP地址的每个部分转换为八位数二进制号码。第二步: 01100. 0.00.001= 213706433(主机字节顺序), 然后将上面的四部分二进制数字从右到左重新编号到第三步00100.00.00.0011=1677343(网络字节顺序)。
例如,考虑港口12345。
第一步,001100 001001001001001=12345(主机字节)端口号,实际上是主机字节序列,从二进制号001100100100=14640(网络字节顺序)的第二步开始,产生一个新的16位数二进制数字,从头八个字节到第二八个字节。网络字节顺序的二进制形式由这个16位字节的新数字二进制数字表示。
因此,如果我们知道12345号港口的网络字节顺序是14640, 我们可以了解更多。 要做到这一点, sin_port=htons( 12345) 可以直接表示为serv_ addr。 结果是一样的, sin_port = htons( 14604). htons的功能是将端口字节转换为网络字节顺序 。
函数 Inet_aton 和 int_enta
Inet_aton 函数将点小数字符串转换为网络地址,而 iint_ento 函数将网络地址转换为点小数字符串。
Int_ aton( 成本字符串字符串, 规则在_ addr * addr); 以字符串表示的网络地址整数表明, 返回的号码总是按照网络字节顺序中的参数表示: 输入参数字符串包括 ASCII IP 地址。 输出参数addr 是将随着新的 IP 地址而改变的结构 。 返回值 : 如果此函数成功, 函数的返回值不是零 。 如果错误输入地址, 返回为零 。 因为使用此函数时没有错误代码保存在错误中, 他的价值会被忽略 。 * int_ entoa (在_ addr) 返回点点二进字符串的指针到网络提供的二进位值 。 方法是将网络中的 IP IP 字串转换为小数字符串匹配 。 到 _ ton 的调为结构的指针, 但它的调与结构本身有关 。
inet_pton/inet_ntop 函数
inet_pton:
支持 IPv4/IPv6 的 IP 地址转换函数可用于将 IP 地址从“ 小数点” 转换为 IP 地址位址 。
inet_ntop:
IP 地址转换功能,由 IPv4/IPv6 支持,将网络字节顺序的互联网地址转换为 " 小数点 " 地址。
nt inet_pton(int af, const char *src, void *dst); 例如 inet_pton(AF_INET, "172.20.223.151", &servaddr.sin_addr); 参数 af:可以是AF_INET(对应的是ipv4)或AF_INET6(ipv6),如果,以不被支持的地址族作为family参数, 这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT. src:是一个指向点分十进制串的指针, dst:是一个指向转换后的网络字节序的二进制值的指针。 返回值 若成功则为1,若输入不是有效的表达式则为0,若出错则为-1,并将errno置为EAFNOSUPPORT. const char *inet_ntop(int af, const void *src,char *dst, socklen_t size); 例如 inet_ntop(AF_INET, &servaddr.sin_addr, IPdotdec, 16); 参数 af:可以是AF_INET(对应的是ipv4)或AF_INET6(ipv6),如果,以不被支持的地址族作为family参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT. src:是一个指向点分十进制串的指针, dst:参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。 size:他是所指向缓存区dst的大小,避免溢出,如果缓存区太小无法存储地址的值,则返回一个空指针,并将errno置为ENOSPC
read函数
ssize_t 读取参数( int fd, 无效的 *buf, size_ t nbites) : sockfd: 连接远程通讯的 sockfd: sockfd: 套接字描述符 : 缓冲地址 接收数据 len: 缓冲区长度
读函数负责从 fd. read 回读 fd. read 中读取实际读读数的字字节数; 如果返回值为 0, 表示所读文档的结尾, 如果网络套接字 fd 表示 TCP 链接中断; 如果小于 0 表示错误, 错误标记存储在错误的全局变量中, 如果错误解释 EINTR 读中断, 如果 EONEST 表示有问题
write函数
ars: Int fildes: 写入文件文件的文件描述, constevale *buf: 存储存储在内存空间大小_ t nbyte 中的数据的地址: 预计写入数据的最大字节数
写入函数将 bff 中的 nbyte 字节内容放入文件描述符中。 返回成功时写入的字节数。 失败时返回一个字节, 并设置错误变量。 当我们写入 Web 程序中的标题页面时, 有两种可能性。 写入的返回值大于 0, 这意味着部分或全部数据是书面的。 返回值小于 0, 并发生了错误 。
网络一/O功能分为以下类别:例如,见人文文件。
ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr,socklen_t addrlen); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t*addrlen); ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
关闭/停放功能
服务器连接到客户端后, 进行各种读和写动作, 相应的套接字描述在读和写活动完成后关闭, 仿佛打开的文件会被关闭关闭关闭。 当 TCP 锁的默认行为关闭并立即返回呼叫程序时, 将挂坠标记为关闭 。
Int close( int fd); int 关闭( int sockfd, 如何) ; 参数 fd: 如何关闭文件 : SUT_ RD 不读取或再次发送数据
客户端代码
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 12345
#define MSG_STR "Hello beautiful world"
int main (int argc, char **argv)
{
int sockfd = -1;
int rv = -1;
struct sockaddr_in serv_addr;
char buf[1024];
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0)
{
printf("Create sockte failure:%sn",strerror(errno));
return -1;
}
printf("Create sockte [%d] successful!n",sockfd);
//服务器地址清零,ipv4,把端口转发网络字节序,点十进制转发为32为整形ip地址
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERVER_PORT);
inet_aton(SERVER_IP,&serv_addr.sin_addr);
rv = connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
if(rv < 0)
{
printf("Failed to connect server:%sn",SERVER_IP,SERVER_PORT,strerror(errno));
return -2;
}
printf("Connecting to the server[%s:%d] successfullyn",SERVER_IP,SERVER_PORT);
while(1)
{
rv = write(sockfd,MSG_STR,strlen(MSG_STR));
if(rv < 0)
{
printf("Write to server[%d] successfully:%sn",sockfd,strerror(errno));
break;
}
memset(buf,0,sizeof(buf));
rv = read(sockfd,buf,sizeof(buf));
if(rv <0 )
{
printf("Failed to read data server:%sn",strerror(errno));
break;
}
else if(0==rv)
{
printf("Connecting to the server:'%s'n",rv,buf);
break;
}
printf("read %d bytes bata from server:'%s'n",rv,buf);
close(sockfd);
}
return 0;
}
服务器端代码
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LISTEN_PORT 12345
#define BACKLOG 8
int main (int argc, char **argv)
{
int rv = -1;
int socket_fd = -1;
int client_fd = -1;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
socklen_t cliaddr_len;
char buf[1024];
int on = 1;
//创建socket ipv4 tcp协议
socket_fd = socket(AF_INET,SOCK_STREAM,0);
if(socket_fd < 0)
{
printf("create socket failure:%sn",strerror(errno));
return -1;
}
printf("socket create fd[%d]n",socket_fd);
//重复端口使用
setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//清零服务器地址,ipv4, 端口网络转换主机字节序,监听所以的ip转换为主机字节序
memset(&serv_addr,0 ,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//绑定端口
if(bind(socket_fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))<0)
{
printf("create socket failure:%sn",strerror(errno));
return -2;
}
printf("socket[%d] bind on port [%d] for all ip address okn",socket_fd,LISTEN_PORT);
//最多BACKLOG连接
listen(socket_fd,BACKLOG);
while(1)
{
printf("n start wainting and accept new client connect.... n",socket_fd);
//监听socket,创建一个新的fd,等待客户端连接,创建新的客户端地址和客户端地址长度,等待
client_fd = accept(socket_fd,(struct sockaddr*)&cli_addr,&cliaddr_len);
if(client_fd <0)
{
printf("accept new socket failure :%sn",strerror(errno));
return -2;
}
//将网络地址转换点十进制地址,把客户端地址的ipv4和客户端端口转发成点十进制
printf("accept new client[%s:%d] wlth fd [%d]n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),client_fd);
memset(buf, 0 ,sizeof(buf));
rv = read(client_fd,buf,sizeof(buf));
if(rv < 0)
{
printf("read data from client socket[%d] failure:%sn",client_fd ,strerror(errno));
close(client_fd);
continue;
}
else if(rv == 0)
{
printf("client socket[%d] disconnectedn",client_fd);
close(client_fd);
continue;
}
printf("read %d bytes bata from client[%d] and echo it back :'%s'n",rv ,client_fd,buf);
rv = write(client_fd,buf,rv);
if(rv < 0)
{
printf("write %d bytes data back to client[%d] failure:%sn",rv ,client_fd,strerror(errno));
close(client_fd);
}
printf("close client socket[%d]n",client_fd);
close(client_fd);
}
close(socket_fd);
return 0;
}
本文由 在线网速测试 整理编辑,转载请注明出处。