Linux - Socket网络套接字

OSI七层协议功能

物理层
面向物理传输媒体,屏蔽媒体的不同
主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的模数转换与数模转换)。这一层的数据叫做比特。 
链路层
面向一条链路,成帧和无差错传输
主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。 
网络层
分配地址、面向多条链路、路由和流量控制
要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。 
传输层
面向两台主机通信,处理可靠传输细节,无差错、无失序传输
定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议),UDP(用户数据报协议)。 主要是将从下层接收的数据进行分段进行传输,到达目的地址后在进行重组。 
会话层
面向一次会话,协调双方的交互、同步
主要在你的系统之间发起会话或或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名) 
表示层
面向通信内容的表示,大家认同的描述方式
主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等)) 
应用层
面向建立在通信基础上的应用,为应用提供通信服务,
主要是一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类的(你就把它理解成我们在电脑屏幕上可以看到的东西.就 是终端应用) 

TCP协议特点

面向连接的可靠传输
端到端、建立/断开连接
正确、顺序传送数据
协议关注问题
IP数据报的丢失、重复、失序、延迟
发送和接收速度的匹配
系统重启动,一方连接信息丢失
网络拥塞

UDP协议特点

无连接
不需要在通信前建立连接
不使用控制报文
传输开销低
面向报文
不将报文分割,也不合并
UDP报文大小直接影响网络利用率
过小造成报头比率过大
过大造成MTU分片
尽力而为、任意交互
一对一、一对多、多对一和多对多

TCP/UDP端口号作为服务器程序标识
服务器启动时,首先在本地主机注册所使用的TCP/UDP端口号
客户通过与服务器指定的TCP端口建立连接(或直接向服务器指定的UDP端口发送信息)来访问特定服务
运行服务器程序的主机收到信息后,将其转交给注册该端口的服务器程序处理

Linux的网络分层结构

BSD Socket是通用接口,支持各种网络工作形式
INET Socket支持包括TCP/IP协议在内的Internet地址族
技术分享

socket基本概念

Socket接口是应用程序与TCP/IP协议栈的接口
定义一组函数/例程,支持TCP/IP网络应用程序开发
与数据通信相关的系统调用是read()/write()
是一种文件描述符
一个套接字描述一个通信连接的一端
两个通信程序中各自有一个套接字来描述自己的
Socket不是TCP/IP标准的组成部分,但已成为事实上的工业标准
UNIX系列系统提供Socket
Windows系列、Macintosh系列、Solaris等亦提供

形式
(IP, PORT)
网络进程标识
<协议, 本地地址, 本地端口>
网间通信标识
<协议, 本地地址, 本地端口, 远程地址, 远程端口>
端口分类
公认端口:从0到1023
小于256的端口作为保留端口
注册端口:端口号从1024到49151 .
动态和/或私有端口:从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024起分配动态端口。

基本socket API

socket()
创建一个新的Socket
close() 
关闭一个Socket
bind() 
将服务器(IP, Port)赋予Socket
listen() 
等待到来的客户连接请求(TCP)
accept() 
接受客户连接请求并建立连接(TCP)
connect() 
向服务器发出连接请求
send()/recv() 
发送/接收数据
字节顺序
大端模式(big   endian):高字节放到高地址上
小端模式(little   endian):高字节放到低地址上
主机字节顺序(HBO,Host Byte Order)
不同的机器HBO不相同,与CPU设计有关
Motorola 68k系列,HBO与NBO相同
Intel x86系列,HBO与NBO相反
网络字节顺序(NBO,Network Byte Order)
使用统一的字节顺序,避免兼容性问题
解决兼容性问题途径
往网络上发送前:转换成网络字节序
从网络接收数据:转换成主机字节序

头文件
#include <netinet/in.h>
函数原型
uint32_t htonl(uint32_t hostlong);
把32位值从主机字节序转换成网络字节序 
uint16_t htons(uint16_t hostshort);
把16位值从主机字节序转换成网络字节序 
uint32_t ntohl(uint32_t hostlong);
把32位值从网络字节序转换成主机字节序 
uint16_t ntohs(uint16_t hostshort);
把16位值从网络字节序转换成主机字节序 
说明
h代表host,n代表 network
s代表short,l代表long 

socket()函数

功能
创建一个套接字
#include <sys/socket.h>
函数原型
int socket(int domain, int type, int protocol);
参数说明
domain:通信协议族,即地址族
type:套接字类型
protocol:通信协议
常设置为0 ,由内核根据指定的类型和协议族使用默认的协议
返回值
成功时,返回一个大于等于0的文件描述符
失败时,返回一个小于0的值

Linux支持的协议和地址族

地址 协议 协议描述
AF_UNIX PF_UNIX Unix域
AF_INET PF_INET TCP/IP(V4)
AF_INET6 PF_INET6 TCP/IP(V6)
AF_AX25 PF_AX25 业余无线电使用的AX.25
AF_IPX PF_IPX Novell的IPX
AF_APPLETALK PF_APPLETALK AppleTalk DDS
AF_NETROM PF_NETROM 业余无线电使用的 NetRom

套接字类型

流套接字(SOCK_STREAM)
可靠的、面向连接的通信
使用TCP协议
数据报套接字(SOCK_DGRAM)
无连接服务
使用UDP协议
原始套接字(SOCK_RAW)
允许对底层协议(如IP、ICMP)直接访问
int main(){
    ……
    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
         perror(“socket”); 
   exit(1); 
    } 
    ……
}
函数原型
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t *optlen)
功能
控制套接字行为,如修改缓冲区大小、传输方式等 
参数说明
level:指定控制套接字的层次
SOL_SOCKET:通用套接字选项
IPPROTO_IP:IP选项
IPPROTO_TCP:TCP选项  
optname:指定控制方式(选项名称)
optval:获得/设置套接字选项值

SOL_SOCKET参数选项

SO_BROADCAST  允许发送广播数据                int 
SO_DEBUG                  允许调试                                 int 
SO_DONTROUTE           不查找路由                              int 
SO_ERROR                 获得套接字错误                     int 
SO_KEEPALIVE             保持连接                                 int 
SO_LINGER                 延迟关闭连接                        struct linger 
SO_OOBINLINE             带外数据放入正常数据流       int 
SO_RCVBUF                 接收缓冲区大小                       int 
SO_SNDBUF                 发送缓冲区大小                       int 
SO_RCVLOWAT            接收缓冲区下限                        int 
SO_SNDLOWAT             发送缓冲区下限                        int 
SO_RCVTIMEO              接收超时                              struct timeval 
SO_SNDTIMEO               发送超时                         struct timeval 
SO_REUSERADDR         允许重用本地地址和端口        int 
SO_TYPE                   获得套接字类型                        int 
SO_BSDCOMPAT           与BSD系统兼容                        int 
IPPROTO_IP
IP_HDRINCL
在数据包中包含IP首部
IP_OPTINOS
IP首部选项
IP_TOS
服务类型 
IP_TTL
生存时间
IPPRO_TCP 
TCP_MAXSEG
TCP最大数据段的大小
TCP_NODELAY
不使用Nagle算法 

套接字选项示例

更改发送/接收缓冲区大小
接收缓冲区
    int nRecvBuf=32*1024;         //设置为32Ksetsockopt(s, SOL_SOCKET, SO_RCVBUF, (const void*)&nRecvBuf, sizeof(int));
发送缓冲区
    int nSendBuf=32*1024;//设置为32Ksetsockopt(s, SOL_SOCKET, SO_SNDBUF, (const void*)&nSendBuf,sizeof(int)); 
说明
对于客户,SO_RCVBUF选项必须在connect之前设置
对于服务器,SO_RCVBUF选项必须在listen前设置 

bind()函数

功能
将套接字地址与所创建的套接字号联系起来
客户端如果只想使用connect(),则无须使用该函数
#include <sys/socket.h>
函数原型
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
参数说明
sockfd:调用socket返回的文件描述符 
my_addr:保存地址信息(IP地址和端口)
addrlen:设置为 sizeof(struct sockaddr)
返回值
成功时,返回0
失败时,返回-1
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #define MYPORT 3490
    int main(){
        int sockfd;
        struct sockaddr_in my_addr;
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons(MYPORT);
        my_addr.sin_addr.s_addr = inet_addr("132.241.5.10");
        bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct 
            sockaddr));
             …….
          }

sockaddr结构定义

功能
保存socket信息
结构
struct sockaddr { 
   unsigned short  sa_family; /* 地址族,AF_xxx */ char sa_data[14];  /* 协议地址 */ 
 }; 
说明
sa_family一般为AF_INET(表示TCP/IP)
sa_data包含socket的IP地址和端口号
/include/linux/socket.h
功能
sockaddr的另一种表示形式
结构
struct sockaddr_in {  short int sin_family;      /* 地址族 */  unsigned short int sin_port; /* 端口号 */  struct in_addr sin_addr;    /* IP地址 */  unsigned char sin_zero[8]; /* 填充0,保持与struct sockaddr等长*/ 
}; 
说明
sin_zero用于将sockaddr_in结构填充到与struct sockaddr等长,可用bzero( )或memset( )函数将其置为0 
当sin_port = 0时,系统随机选择一个未被使用的端口号
当sin_addr = INADDR_ANY时,表示填入本机IP地址 
指向sockaddr_in的指针和指向sockaddr的指针可相互转换

connect()函数

功能
建立套接字连接 
#include <sys/socket.h>
函数原型
int  connect(int  sockfd,  const struct sockaddr *serv_addr, socklen_t addrlen);
参数说明
sockfd:调用socket返回的文件描述符
serv_addr:远程主机IP 地址和端口
addrlen:设置为 sizeof(struct sockaddr)
返回值
成功时,返回0
失败时,返回-1
   #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #define DEST_IP "132.241.5.10"
    #define DEST_PORT 23
    int main() {
        int sockfd;
        struct sockaddr_in dest_addr;
        sockfd = socket(AF_INET, SOCK_STREAM, 0); 
        dest_addr.sin_family = AF_INET; 
        dest_addr.sin_port = htons(DEST_PORT); 
        dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
        connect(sockfd, (struct sockaddr*)&dest_addr, 
        sizeof(struct sockaddr));

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。