套接字(socket)编程简介
套接字(socket)编程简介
现在的网络编程几乎都是用的socket。
我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?
这些都得靠socket!那什么是socket?下面介绍一下socket的相关概念和一些基本函数。
套接字概念
Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。
既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。
在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。
套接字通信原理如下图所示:
在网络通信中,套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。
TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。本文的主要内容是socket API,主要介绍TCP协议的函数接口,最后介绍UDP协议和UNIX Domain Socket的函数接口。
应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为**套接字(Socket )**的接口,区分不同应用程序进程间的网络通信和连接。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用以下模式来操作
“打开open –> 读写write/read –> 关闭close”
Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭),这些函数我们在后面进行介绍。
生成套接字,主要有3个参数:通信的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。
Socket 原意是“插座”。通过将这3个参数结合起来,与一个“插座”Socket 绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
TCP/IP协议族包括运输层、网络层、链路层,而socket所在位置如图,Socket是应用层与TCP/IP协议族通信的中间软件抽象层
sockaddr数据结构
strcut sockaddr 很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结构体,为了向前兼容,现在sockaddr退化成了(void *)的作用,传递一个地址给函数,至于这个函数是sockaddr_in还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型。
sockaddr数据结构:
struct sockaddr {sa_family_t sa_family; //地址结构类型char sa_data[14]; //地址数据, 14 字节的协议地址,sa_data则包含该socket的IP地址和端口号
};/*说明:
在实际编程中,一般定义struct sockaddr_in addr,
然后给各个成员赋值,传参数强制转换为struct sockaddr, 例如 (struct sockaddr *) &addr*/
IPv4: struct sockaddr_in (internet), 16个字节
struct sockaddr_in {__kernel_sa_family_t sin_family; //地址结构类型,AF_INET__be16 sin_port; //端口号struct in_addr sin_addr; //IP地址/* Pad to size of `struct sockaddr'. */unsigned char sin_zero[sizeof (struct sockaddr) -sizeof (sa_family_t) -sizeof (in_port_t) -sizeof (struct in_addr)];
};//其中ip地址封装了32位的地址信息--对应点分十进制
struct in_addr { __be32 s_addr;
};
IPv6: struct sockaddr_in6, 28个字节
struct sockaddr_in6 {unsigned short int sin6_family; //地址结构类型,AF_INET6__be16 sin6_port; //端口号__be32 sin6_flowinfo; //流量信息struct in6_addr sin6_addr; //IP地址__u32 sin6_scope_id; //scope_id
};struct in6_addr {union {__u8 u6_addr8[16];__be16 u6_addr16[8];__be32 u6_addr32[4];} in6_u; #define s6_addr in6_u.u6_addr8#define s6_addr16 in6_u.u6_addr16#define s6_addr32 in6_u.u6_addr32
};
struct sockaddr_un, 110字节
#define UNIX_PATH_MAX 108struct sockaddr_un {__kernel_sa_family_t sun_family; /* AF_UNIX */char sun_path[UNIX_PATH_MAX]; /* pathname */
};
Pv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位端口号和32位IP地址,IPv6地址用sockaddr_in6结构体表示,包括16位端口号、128位IP地址和一些控制字段。UNIX Domain Socket的地址格式定义在sys/un.h中,用sock-addr_un结构体表示。各种socket地址结构体的开头都是相同的,前16位表示整个结构体的长度(并不是所有UNIX的实现都有长度字段,如Linux就没有),后16位表示地址类型。IPv4、IPv6和Unix Domain Socket的地址类型分别定义为常数AF_INET、AF_INET6、AF_UNIX。这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。因此,socket API可以接受各种类型的sockaddr结构体指针做参数,例如bind、accept、connect等函数,这些函数的参数应该设计成void *类型以便接受各种类型的指针,**但是sock API的实现早于ANSI C标准化,那时还没有void 类型,因此这些函数的参数都用struct sockaddr 类型表示,在传递参数之前要强制类型转换一下,例如:
struct sockaddr_in servaddr;bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr)); /* initialize servaddr */
网络字节序与主机字节序
主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
网络字节序:网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。
所以,在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。
字节顺序转换函数
头文件:#include <arpa/inet.h>
·htonl():把32位值从主机字节序转换成网络字节序
·htons():把16位值从主机字节序转换成网络字节序
·ntohl():把32位值从网络字节序转换成主机字节序
·ntohs():把16位值从网络字节序转换成主机字节序
1. uint32_t htonl(uint32_t hostint32);
功能:
将 32 位主机字节序数据转换成网络字节序数据
参数:
hostint32:需要转换的 32 位主机字节序数据,uint32_t 为 32 为无符号整型
返回值:
成功:返回网络字节序的值
2. uint16_t htons(uint16_t hostint16);
功能:
将 16 位主机字节序数据转换成网络字节序数据
参数:
hostint16:需要转换的 16 位主机字节序数据,uint16_t,unsigned short int
返回值:
成功:返回网络字节序的值
3. uint32_t ntohl(uint32_t netint32);
功能:
将 32 位网络字节序数据转换成主机字节序数据
参数:
netint32:待转换的 32 位网络字节序数据,uint32_t,unsigned int
返回值:
成功:返回主机字节序的值
4. uint16_t ntohs(uint16_t netint16);
功能:
将 16 位网络字节序数据转换成主机字节序数据
参数:
netint16:待转换的 16 位网络字节序数据,uint16_t,unsigned short int
返回值:
成功:返回主机字节序的
IP地址转换
头文件:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
1. int inet_pton(int family, const char *strptr, void *addrptr);
功能:
将点分十进制数串转换成 32 位无符号整数
参数:
family:协议族( AF_INET、AF_INET6、PF_PACKET 等 ),常用 AF_INET
strptr:点分十进制数串
addrptr:32 位无符号整数的地址
返回值:
成功返回 1 、 失败返回其它
2. const char *inet_ntop( int family, const void *addrptr, char *strptr, size_t len );
功能:
将 32 位无符号整数转换成点分十进制数串
参数:
family:协议族( AF_INET、AF_INET6、PF_PACKET 等 ),常用 AF_INET
addrptr:32 位无符号整数
strptr:点分十进制数串
len:strptr 缓存区长度
len 的宏定义
#define INET_ADDRSTRLEN 16 // for ipv4
#define INET6_ADDRSTRLEN 46 // for ipv6
返回值:
成功:则返回字符串的首地址
失败:返回 NULL
3. in_addr_t inet_addr(const char * cp)
inet_addr函数转换网络主机地址(如192.168.1.10)为网络字节序二进制值,如果参数char *cp无效,函数返回-1(INADDR_NONE),这个函数在处理地址为255.255.255.255时也返回-1,255.255.255.255是一个有效的地址,不过inet_addr无法处理
4. char *inet_ntoa(struct in_addr in)
inet_ntoa 函数转换网络字节排序的地址为标准的ASCII以点分开的地址,该函数返回指向点分开的字符串地址的指针,该字符串的空间为静态分配的,这意味着在第二次调用该函数时,上一次调用将会被重写(复盖)
应用举例:
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h> int a = 0x01020304;
short int b = 0x0102;
printf("htonl(0x%08x) = 0x%08x\n", a, htonl(a));
printf("htons(0x%04x) = 0x%04x\n", b, htons(b));char ip_str[]="172.20.223.75";
unsigned int ip_uint = 0;
unsigned charchar *ip_p = NULL;
inet_pton(AF_INET,ip_str,&ip_uint);
printf("in_uint = %d\n",ip_uint); unsigned char ip[] = {172,20,223,75};
char ip_str[16] = "NULL";
inet_ntop(AF_INET,(unsigned intint *)ip,ip_str,16);
printf("ip_str = %s\n",ip_str); strcut sockaddr_in add;
add.sin_addr.s_addr = inet_addr("*.*.*.*"); //构建网络地址。
printf("ip is %s\n",inet_ntoa(add.sin_addr)); char *add1,add2;
src.sin_addr.s_addr = inet_addr("192.168.1.123");
add1 =inet_ntoa(src.sin_addr);
src.sin_addr.s_addr = inet_addr("192.168.1.124");
add2 = inet_ntoa(src.sin_addr);
总结:
struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式。这两个结构体一样大,都是16个字节,而且都有family属性,不同的是:
sockaddr用其余14个字节来表示sa_data,而sockaddr_in把14个字节拆分成sin_port, sin_addr和sin_zero
分别表示端口、ip地址。sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小。
sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别:
程序员不应操作sockaddr,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中,sockaddr是给操作系统用的
程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便
字的地址形式。这两个结构体一样大,都是16个字节,而且都有family属性,不同的是:
sockaddr用其余14个字节来表示sa_data,而sockaddr_in把14个字节拆分成sin_port, sin_addr和sin_zero
分别表示端口、ip地址。sin_zero用来填充字节使sockaddr_in和sockaddr保持一样大小。
sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别:
程序员不应操作sockaddr,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中,sockaddr是给操作系统用的
程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便
套接字(socket)编程简介相关推荐
- Java套接字Socket编程--TCP参数
在Java的Socket中,主要包含了以下可设置的TCP参数. 属性 说明 默认值 SO_TIMEOUT 对ServerSocket来说表示等待连接的最长空等待时间; 对Socket来说表示读数据最长 ...
- 套接字Socket编程
Socket,原意插座.插口.写软件程序时,可以想象成一根网线,一头插在客户端,一头插在服务端,然后进行通信.所以通信前,双方都要建立一个Socket. Socket编程进行的是端到端的通信,意识不到 ...
- [python学习] 专题七.网络编程之套接字Socket、TCP和UDP通信实例
很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 ...
- 网络编程:套接字socket函数与绑定信息bind函数
套接字socket函数与绑定信息bind函数 套接字 绑定信息(绑定IP和端口) socket函数 bind函数 struct sockaddr结构体的组成: struct sockaddr_in结构 ...
- Java网络编程入门,包含网络相关概念、InetAddress类、套接字Socket、网络上传和下载文件等
Java学习-11-韩顺平老师 Java-网络编程入门 目录: 01-网络相关概念 02-InetAddress类 03-套接字Socket 04-网络上传和下载文件 05-UDP网络编程 网络编程相 ...
- java实现套接字网络编程_Java网络编程(一)Socket套接字
一.基础知识 1.TCP:传输控制协议. 2.UDP:用户数据报协议. 二.IP地址封装 1.InetAddress类的常用方法 getLocalHost() 返回本地主机的InetAddress对象 ...
- 网络编程(网络基础、套接字Socket、数据报Datagram及其常用方法)
章节内容 套接字Socket 数据报Datagram 章节目标 了解网络通信中的IP,端口和协议 掌握套接字的使用 熟悉数据报的使用 一.网络基础 1.软件结构 C/S结构 C => Clien ...
- Linux下套接字详解(二)----套接字Socket
在前面我们讲了TCP/IP.TCP和UDP的一些基本知识,但是协议只有一套,而我们系统多个TCP连接或多个应用程序进程必须通过同一个 TCP协议端口传输数据.为了区别不同的应用程序进程和连接,许多计算 ...
- 安卓学习笔记40:基于套接字网络编程
文章目录 零.学习目标 一.Socket概述 (一)两种传输模式 (二)基于Socket网络编程 三.案例演示 - C/S架构聊天室 (一)运行效果 (二)涉及知识点 (三)实现步骤 1.创建聊天服务 ...
- 套接字socket 的地址族和类型、工作原理、创建过程
注:本分类下文章大多整理自<深入分析linux内核源代码>一书,另有参考其他一些资料如<linux内核完全剖析>.<linux c 编程一站式学习>等,只是为了更好 ...
最新文章
- 收藏 | 深度学习中神经网络的可视化解释!
- vim-addon-manager install youcompleteme
- php 获取用户的IP、地址、来源
- 中文只占一个字符_一文搞懂字符和字节的含义
- 【Python图像特征的音乐序列生成】解析ABC格式的文件(修改版)
- ora-01591:锁被未分布式事物处理/Distrib tran
- 《ASP.NET Core 真机拆解》 送书活动结果公布
- MySQL find_in_set()函数
- iOS应用软件沙盒sandbox相关知识(整理)
- compareto方法_Java ArrayList 的不同排序方法
- C语言排序方法-----冒泡排序法
- vue如何让自定义函数挂到全局
- 简单介绍Javascript匿名函数和面向对象编程
- delphi之鼠标模拟
- ldpcMATLAB/ldpc的译码,matlab程序/LDPC编码的matlab实现/源码
- php网页源码库存管理系统进销存mysql数据库web结构html布局
- javaSE基础全覆盖
- 四十八 停电与打牌(中) 我在软件园的那些日子里
- linux 排除多个目录搜索文件,关于linux:使用find命令但排除两个目录中的文件
- TSP 问题的几种经典建模方式