“套接字”或者“插座”也是一种软件形式的抽象 ,用于表达两台机器之间连接的终端,
对于一个特定的连接,每台机器上都有一个套接字,可以想象它们之间有一条虚拟的“线缆”,线缆的每一端都插入一个”套接字“或者”插座“里。
应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序提供并发服务的问题,多个TCP连接或者多个应用程序进程可能 需要通过同一个TCP协议端口传输数据,为了区别不同的应用进程和连接,许多计算机系统为应用程序与TCP/IP协议交互提供了套接字的接口,区分不同应用程序进程间的网络通信和连接。
生成套接字:
通信的目的IP地址,使用的传输层协议(TCP/UDP)和使用的端口号,
将这三个参数结合起来,与一个插座绑定,应用层就可以和传输层通过套接字接口 ,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务
要通过互联网进行通信,至少需要一对套接字,一个运行于客户端,另一个运行于服务器。
步骤:
1.服务器监听:是服务器端套接字,并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
2.客户端请求:指客户端套接字提出连接请求,要连接的目标是服务器套接字,为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
3.连接确认:服务器端监听或者接收到客户端套接字的连接请求,就响应客户端套接字的
请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了,而服务器端套接字继续处于监听状态,继续接收其他客户端套结字的连接请求。
TCP(面向连接编程)
2.无连接编程(UDP)
Linux 下getsockopt /setsockopt 函数说明
通过调整套接字属性,能够实现广播,组播,地址重用,数据缓冲区以及数据超时处理等功能
功能:获取或者设置某个套接字关联的选项,选项可能存在于多层协议中,它们总是会出现在最上面的套接字层,当操作套接字选项时,选项位于的层和选项的名称必须给出,
#include
#include
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);
第一个参数:用于指定要操作的套接字
第二个参数:选项所在的协议层
第三个参数 :需要访问的选项名
第四个参数:对于getsockopt(),指向返回选项值的缓冲,对于setsockopt(),指向包含新选项的缓冲。
第五个参数:对于getsockopt(),作为入口参数时,选项值的最大长度,作为出口参数时,选项的实际长度,对于setsockopt(),现选项的长度
成功返回0,失败返回-1,errno被设置为下列值:
EBADF :socket 不是有效的文件描述符
EFAULT :optval指向的内存并非有效的进程空间
EINVAL:optlen 无效
ENOPROTOOPT:指定的协议层不能识别
ENOTSOCK:sock描述的不是套接字
参数说明:
Level 指定控制套接字的层次:
1)SOL_SOCKET :通用套接字选项
2)IPPROTO_IP :IP选项
3)IPPROTP_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首部 int
IP_OPTINOSIP首部选项 int
IP_TOS服务类型
IP_TTL 生存时间 int
========================================================================
IPPRO_TCP
------------------------------------------------------------------------
TCP_MAXSEGTCP最大数据段的大小 int
TCP_NODELAY 不使用Nagle算法 int
========================================================================
SO_RECVBUF 和SO_SNDBUF每个套接口都有一个发送缓冲区和接收缓冲区,使用这两个套接口的选项可以改变缺省缓冲区的大小,
// 接收缓冲区
int nRecvBuf=32*1024; //设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
当设置TCP套接口缓冲区的大小时,函数调用顺序很重要,因为TCP 的窗口规模选项是建立在连接时用SYN与对方互换得到的,对于客户,O_RECVBUF 选项必须在connect 之前设置, 对于服务器,SO_RECVBUF 选项必须在listen 前设置
原理:
每个套接口都有一个发送缓冲区和一个接收缓冲区,接收缓冲区被TCP和UDP用来接收到的数据一直保存到由应用进程来读,
TCP:
TCP套接口接收缓冲区不可能溢出,因为对方不允许发出超过所通告窗口大小的数据,这就是TCP 的流量控制 ,如果对方无视窗口大小而发出超过窗口大小的数据,则接收方TCP将会丢弃它。
UDP:
当接收到的数据装不进套接口的接收缓冲区时,此数据报就会被丢弃,UDP是没有流量控制的,快的发送者可以很容易的淹没慢的接收者,导致接收方的UDP丢弃数据报
Setsockopt 的用法:
(1)closesocket (一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket
BOOL Reuseaddr = TRUE;
Setsockopt(s,SOL_SOCKET, SO_REUSEADDR, (const char*)&Reuseaddr, sizeof(BOOL));
(2如果要已经处于连接状态的socket在调用closesocket后强制关闭,不经历TIME_WAIT 的过程
BOOL bDontLiner = FALSE;
Setsockopt(s, SOL_SOCKET, SO_DONTLINER, (const char*)&bDontLiner, sizeof(BOOL));
(3)在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限
Int nNetTime = 1000
Setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&nNetTime, sizeof(int));设置发送时限
Setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&nNetTime, sizeof(int));设置接收时限
(4)在send()的时候,返回的是实际上发出去的字节(同步)或者发送到socket缓冲区的字节(异步);系统默认的状态发送和接收一次为8688字节,在实际的发送过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发。
//接收缓冲区
Int nRecvBuf = 32*1024;
Setsockopt(s, SOL_SOCKET, SO_RECVBUF, (char*)&nRecvBuf, sizeof(int));
//发送缓冲区
Int nsendBuf = 32*1024;
Setsockopt(s, SOL_SOCKET, SO_SNEBUF, (char*)&nsendBuf, sizeof(int));
(5)如果在发送数据时,不希望经历从系统缓冲区到socket 缓冲区的拷贝而影响程序的性能时
Int nZero = 0;
Setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char*)&nZero, sizeof(nZero));
(6)在recv()上完成上述功能(默认是从socket缓冲区的内容拷贝到系统缓冲区)
Int nZero = 0;
Setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&nZero, sizeof(nZero));
(7)一般在发送UDP数据报时,希望该socket发送的数据具有广播特性
BOOL bBroadcast = TRUE;
Setsockopt(s, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(BOOL));
(8)在client连接服务器过程中,如果处于非阻塞模式下socket在connect()过程中可以设置connect()延时,直到accept()被呼叫
该函数的设置只有在非阻塞的过程中有显著的作用,对于阻塞函数的作用不大
BOOL bConditionalAccept = TRUE;
Setsockopt(s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char *)&bConditionalAccept, sizeof(BOOL));
(9)如果在发送数据的过程中send()没有完成,还有数据没发送,而调用了closesocket(),怎样设置让没发送完成的数据发送出去,然后再关闭socket?
Struct linger
{
u_short l_onoff;
u_short l_linger;
};
linger m_sLinger;
m_sLinger.l_onoff = 1;(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)
如果m_sLinger.l_onoff = 0;则功能和(2)相同
m_sLinger.l_linger = 5;(容许逗留的时间为5秒)
Setsockopt(s, SOL_SOCKET, SO_LINGER, (const char*)&m_sLinger, sizeof(linger));
原始套接字:
原始套接字是一种套接字的底层技术,它工作在网络层,原始套接字可已完成以下功能:
1.设置网卡为混杂模式,嗅探当前网络 流经本网卡所得数据包
2.构造各种数据包(IP, ICMP, TCP, UDP),并进行发送
3.进行新协议的验证
原始套接字的创建:
Int rawsock = socket(AF_INET, SOCK_RAW, htons(ETH_P_TP));
协议码 协议名
IPPROTO ICMP 协议
ETH_P_IP IP协议
IPPROTO_TCP TCP 协议
IPPROTO_UDP UDP 协议