Socket编程实践(1) 基本概念
2016-04-24 11:46
453 查看
1. 什么是socket
socket可以看成是用户进程与内核网络协议栈的编程接口。TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间。用户空间的程序需要通过套接字来访问内核网络协议栈。套接口是全双工的通信,它不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信。
套接字还可以异构系统间进行通信,异构系统指的是在硬件或软件上有所差别的系统,例如安卓系统的手机与windows系统的PC机上都可以实现QQ通信,套接字可以实现在这两个设备上的通信。
2. IPV4套接口地址结构
套接口既然能够连接两个端系统,那它就需要一个地址来标记该端系统,例如两个电话需要电话号码来标记才可以进行拨号。这抽象成套接口的地址结构。IPV4套接口地址结构通常也称为“网际套接字地址结构”,它以sockaddr_in命名,定义在头文件< netinet/in/h >中。struct sockaddr_in{ uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero[8]; };
说明:
sin_len:整个sockaddr_in结构体的长度,在4.3BSD-Reno版本之前的第一个成员是sin_family。
sin_family:指定带地址家族,在这里必须设置为AF_INET。socket在设计时不仅可以用于TCP/IP协议,它还可以用于其他协议,例如unix域协议,地址家族用于指定该套接字用于哪种协议。AF_INET表示用于IPV4协议。
sin_port:端口号,16位的无符号整数,能够表示到65535。2个字节。
sin_addr: IPV4的地址。4个字节的整数。
sin_zero:暂不使用,一般将其设置为0。
其中,struct in_addr仅仅是一个32位的无符号整数,可以在终端下输入man 7 ip进行查看:
接下来看一下通用的地址结构。上面说过,socket可以用于不同的协议上,通用的地址结构可以用于任何协议的socket编程。
struct sockaddr{ uint8_t sin_len; sa_family sin_family; char sa_data[14]; };
说明:
sin_len:整个sockaddr结构大小
sin_family:指定该地址家族
sa_data:由sin_family决定它的形式
可以看到,在通用地址结构中sa_data是14个字节,而在IPV4的地址结构中,sin_port、sin_addr、sin_zero三个变量加起来也等于14个字节。也即是说,这两种结构是兼容的。
3. 网络字节序
字节序可以分为大端字节序与小端字节序:大端字节序(Big Endian) :最高有效位存储于最低内存地址处,最低有效位存储于最高地址内存处。
小端字节序(Little Endian):刚好与大端字节序倒过来,最高有效位存于最高内存地址处,最低有效位存储于最低内存地址处。
这样说起来挺抽象,通过一幅图来说明:
上面说过,socket可以用于异构系统之间的通信。而不同的系统采用的字节序可能是不同的,有的系统采用大端字节序,例如Motorola 6800;有的采用小端字节序,如X86。因此,在进行字节传输时,应该同一一个字节序,称为网络字节序。网络字节序采用大端字节序。如果主机A为小端字节序的系统,那么在传输时需要先将小端字节序转换成网络字节序。这需要一些字节序的转换函数。
我们可以编写程序来测试自己的主机是什么字节序:
#include<stdio.h> int main(void) { unsigned int x = 0x12345678; unsigned char *p = (unsigned char*)&x; printf("%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]); return 0; }
在我的电脑上输出结果为:78,56,34,12. 因此我的主机为小端字节序。
4. 字节序转换函数
如果主机的字节序与网络字节序不同,那么需要进行字节序的转换。下面是一些字节序转换函数:# include < arpa/inet.h > uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);
说明:h代表host;n代表network;s代表short;l代表long
描述:
htonl()函数将无符号整数hostlong从主机字节序转换成网络字节序。
htons()函数将无符号短整型hostshort从主机字节序转换成网络字节序。
ntohl()函数功能与 htonl()函数相反
ntohs()函数功能与htons()函数相反
我们可以进行验证,刚才已经通过程序测试出我的主机是小端字节序,接下来使用函数 htonl()将整数0x12345678转换成网络字节序。
#include<stdio.h> #include <arpa/inet.h> int main(void) { unsigned int x = 0x12345678; unsigned char *p = (unsigned char*)&x; printf("转换前:%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]); unsigned int y = htonl(x); p = (unsigned char *) &y; printf("转换后:%0x,%0x,%0x,%0x\n",p[0],p[1],p[2],p[3]); return 0; }
结果输出:
转换前:78,56,34,12
转换后:12,34,56,78
5. 地址转换函数
对于IP地址,我们通常采用点分十进制的形式进行直观的认识,而程序更多的时候是处理32位的地址,因此需要有函数在点分十进制与32位地址这两种形式间进行转换。# include < sys/socket.h> # include < netinet/in.h> # include < arpa/inet.h> int inet_aton(const char *cp, struct in_addr *inp); in_addr_t inet_addr(const char *cp); char *inet_ntoa(struct in_addr in);
描述:
inet_addr()函数:表示将点分十进制的IP地址转换成32位的ip地址(整数)。
inet_ntoa()函数:将32位ip地址(网络字节序)转换成点分十进制的ip之地。
例程:
#include<stdio.h> #include<arpa/inet.h> int main() { unsigned long addr = inet_addr("192.168.0.100");//将点分十进制转换为32bit地址 printf("addr = %u\n",htonl(addr)); struct in_addr ipaddr; ipaddr.s_addr = addr; printf("ipaddr = %s\n",inet_ntoa(ipaddr)); //网络字节序地址转换为点分十>进制 return 0; }
输出:
addr = 3232235620
ipaddr = 192.168.0.100
6. 套接字类型
套接字类型主要有三种:流方套接字(SOCK_STREAM):它对应TCP协议,它提供面向连接的、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。
数据报套接字(SOCK_DGREAM):提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。
原始套接字(SOCK_RAW):它提供一种能力,让我们直接跨越传输层,直接对IP层进行数据封装,通过该套接字,我们可以直接将数据封装成IP层能够认识的协议格式。
文章连接:http://www.cnblogs.com/QG-whz/p/5426634.html
相关文章推荐
- Java中的transient,volatile和strictfp关键字
- [C++]polymorphic and OOP design pattern Abstract Factory(抽象工厂模式)
- 关于异步,同步,阻塞与非阻塞
- ipython and bpython
- windows环境下在python中用opencv
- java中关于异常得相关知识整理
- Java中的多线程与同步
- git 远程操作
- 视频教程:卡尔曼滤波器的原理以及在MATLAB中的实现
- spring之Hooking to bean life cycles
- SPRING与MYbatis的整合
- 两个栈实现队列(Java)
- python常见问题与解答
- 基础知识(十六)Opencv、python、ubuntu
- 使用github常用问题汇总
- Php连接mysql实现注册信息和文件上传
- java反射
- C++作业4
- Java实现大数运算
- java Annotation(注解)(一)