阅读 219

socket知识点分享

1.socket概念:

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。

说明:socket是应用层与传输层之间的一个抽象层,它把TCP/IP复杂的操作抽象为几个简单的接口供应用层调用。

2.一起了解一下socket通信流程(请看下图,此图是我随地捡的,如有遗失,请别找我)


建立连接需要一对socket,其中一个运行于客户端,称为ClientSocket,一个运行服务端,称为ServerSocket。

服务端说明:服务端通过调用bind()把指定IP地址和端口号进行绑定后,通过调用listen()函数使socket进入被动监听状态(一开始创建socket都是主动状态,listen()作用就是使服务端socket从主动连接状态变为被动连接状态),内核会创建SYN队列以及Accept队列,其中Accept队列的长度是由listen()函数中的backlog参数进行指定的,然后可以通过调用accept()函数接收客户端请求,在调用了accept()函数后,默认是阻塞进程的,直到有一个客户端请求连接,连接成功后返回一个新的socket描述符,此后,服务器端即可使用这个新的socket描述符与该客户端进行通信了,而旧的socket描述符则继续用于监听其他客户端的连接请求。

客户端说明:客户端通过调用connect()连接指定服务端socket时,将会发起一个连接请求的同步序列编号(SYN:是TCP/IP建立连接时的握手信号),服务器端在接收到客户端发送过来的连接请求的时候会将请求方放进SYN队列里,并且给客户端回复一个ACK+SYN,这称为第二次握手,客户端在收到ACK+SYN之后,connect将返回,并发送确认连接帧ACK给服务器端,这是第三次握手,服务端收到ACK帧后,会将请求方从SYN队列中移出,放入到Accept队列中,而accept也等到了自己的资源,从阻塞中唤醒,从Accept中取出请求方,重新建立并返回一个新的socket描述符。

socket缓冲区(阻塞模式)

socket被创建后,都会被分配两个缓冲区,输入缓冲区和输出缓冲区。

输入缓冲区:数据的读取都是从输入缓冲区中进行读取的。

输出缓冲区:数据的写入都是写入到输出缓冲区中。

write()/send()并不会立刻向网络中传输数据,而是将数据写入到输出缓冲区,再由TCP协议发送到目标机器,只要数据写到缓冲区,write()/send()就可以成功返回,而不管数据有没有到达目标机器,也不管什么时候发送数据。这是TCP负责的,不是程序员可以控制的。

(1).当使用write()/send()向缓冲区写数据的时候,如果输出缓冲区可用空间比要写入数据小,则需要分批写入,write()/send()将被阻塞,需要等到缓冲区数据被发送到目标机器,腾出空间才会唤醒write()/send()继续写入操作。

(2).如果当前TCP正在向网络发送数据,输出缓冲区将会被锁定,write()/send()也会被阻塞,只有数据发送完成,输出缓冲区解锁,write()/send()才会被唤醒继续写入操作。

(3).直到所有数据被写入输出缓冲区write()/send()才会返回。

read()/recv()读取数据时:

(1).当使用read()/recv()读取输入缓冲区数据的时候,如果输入缓冲区没有数据,那么read()/recv()将会被阻塞,直到输入缓冲区中有数据可用读取,否则read()/recv()将一直被阻塞。

(2).如果要读取数据长度小于输入缓冲区中数据长度,那么就不能一次性将缓冲区中数据全部读取完成,剩余数据将不断积压,直到下一次read()/recv()再次读取。

3:一起了解一下socket编程中的一些函数介绍

/**
 创建一个socket
 int af:指定IP地址类型(常用的有AF_INET和AF_INET6)
 int type:指定socket类型(常用的有SOCK_STREAM和SOCK_DGRAM)
 int protocol:表示传输协议(常用的有IPPROTO_TCP和IPPTOTO_UDP,如果传入0,则会自动根据第二个参数
                        ,选中合适的协议)
 return:成功返回正值   失败返回-1
*/
int socket( int af, int type, int protocol);复制代码

/**
 用于建立与指定socket的连接
 int sock:未连接socket描述符
 struct sockaddr *serv_addr:指向数据结构sockAddr的指针,其中包括目的地端口和IP地址
 socklen_t addrlen:为serv_addr结构体变量大小
 return:成功返回0   失败返回-1
*/
int connect(int sock, struct sockaddr *addr, socklen_t addrlen);复制代码

/**
 服务器通过bind()函数将socket与特定的IP地址和端口绑定起来
 int sock:未连接socket描述符
 struct sockaddr *addr:指向数据结构sockAddr的指针
 socklen_t addrlen:为serv_addr结构体变量大小
 return:绑定成功返回0  失败返回-1
*/
int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
复制代码

/**
 listen()用于让socket进入被动监听状态(刚开始创建的socket都是主动状态)
 int sock:需要进入监听状态的socket描述符
 int backlog:accept队列最大长度,如果将backlog的值设置为SOMAXCONN,就由系统决定accept队列长度
 return 成功返回0   失败返回-1
*/
int listen(int sock, int backlog);复制代码

/**
 当socket处于监听状态时,可以通过accept()函数接收客户端请求
 int sock:socket描述符
 struct sockaddr *addr:这是一个结果参数,它用来接受一个返回值,这返回值指定客户端地址
 socklen_t *addrlen:addrlen 为参数 addr 的长度
 return:返回一个新的socket描述符(此后与对应客户端进行通信都是使用这个新的socket)
*/
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
复制代码

/**
 向TCP连接的另一端发送数据
 int sock:socket描述符
 const char FAR *buf:要发送数据的缓冲区
 int len:实际要发送数据字节数
 int flags:标志位默认为0
 return:成功返回所发数据字节数
*/
int send( int sock, const char FAR *buf, int len, int flags ); 
复制代码

/**  
 使用recv函数从TCP连接的另一端接收数据。
 int sock:socket描述符
 char FAR *buf:指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据
 int len:指明buf的长度;
 int flags:标志位默认为0*/
int recv(int sock,char FAR *buf,int len,int flags);复制代码

借鉴资料: blog.51cto.com/ticktick/77…

终于总结完了,还是有个别知识点理解得不是很透彻,需要反复阅读和理解消化。希望各位小伙伴多多提出宝贵意见,交流学习~


关注下面的标签,发现更多相似文章
评论