socket基础之c/s通信过程
2017-04-27 22:36
316 查看
socket可以看成是用户进程与内核网络协议栈的编程接口,socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程通信。
这里所说的不同主机例如手机与PC的通信,为什么说手机与PC为不同主机?只是因为其外观不同吗?不是的,首先手机与PC的软件不同,其次是两者的硬件架构不同,手机采用的是ARM架构而PC大多是x86的架构(至于这两者架构有什么区别,博主现在还不了解,待后续补充,先自行查看参考资料)
下图为client(用户)与server(服务器)之间的通信过程。用到了socket(),connect(),bind(),listen(),accept(),write(),read()等函数。
![](http://img.blog.csdn.net/20170423125500685?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2MzNDgzNDIyMTU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
大端字节序:最高有效位存储于最低内存地址处,最低有效位存储于最高内存地址。
小端字节序:最高有效位存储于最高内存地址处,最低有效位存储于最低内存地址。
主机字节序:不同的主机有不同的字节序。
(1)x86硬件架构下的主机为小端字节序
(2)Motorola 68000为大端字节序
(3)ARM架构的主机字节序是可配置的
网络字节序:网络字节序规定为大端字节序。
异构系统传输规定将主机A的字节序转化为网络字节序,再将网络字节序转化为主机B的字节序。(重要)(这里所说的异构系统可以理解为不同架构的系统,上面说的主机字节序因为架构不同,所产生的字节序也不同,所以规定统一先转化为网络字节序)
2
3
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
3
上面这两种均返回网络字节序的值
2
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
上面这两种均返回主机字节序的值
h代表host n代表network s代表short l代表long
这些函数后续博客都会详细说明
对字节序有一定了解后我们来说socket套接字地址结构,定义在
IPv4套接字地址结构如下:
2
3
4
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
3
4
2
3
4
5
6
7
8
9
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
3
4
5
6
7
8
9
2
3
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
3
2
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
int type参数指明套接字类型:
2
3
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
3
intprotocol参数指明协议类型(也可设置为0,当设置为0时选择family和type组合来确定传输协议):
2
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
2
3
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
3
bind函数作用:将IP、端口命名给socket,让它具有远程通信的功能。
const struct sockaddr*myaddr参数是取特定协议地址结构(例如IPv4的sockaddr_in)
socklen_t addrlen参数表示所指向的特定协议地址结构的结构体大小
一般情况下客户端没必要使用bind绑定socket,因为在发送数据报文的时候,内核会帮你填写好报文头部的源IP和源端口,但是需要在client中初始化服务器端的IP和端口,这样才能在connect之后将报文发送至目的IP和目的端口。bind函数一般用于服务器,服务器端的第二个结构体参数不用初始化,因为当服务器accept时,服务器端接受报文后会将报文头部的源IP和源端口写入服务器的structsockaddr_in结构体中。
2
3
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
3
int backlog参数指的是请求队列的最大长度(这是一个幻数,自己定义的请求队列的大小是一个值,但实际上可能允许的最大队列大于自己定义的这个值,kernel2.2之前这个参数代表了未完成与完成连接的个数,kernel2.6之后为完成建立连接的最大值)
listen函数仅由TCP服务器调用,所谓监听是指当没有客户端(client)的connect函数请求时,套接字处于“睡眠”状态,只有当接收到client的connect请求时,socket套接字才被“唤醒”来响应请求。注意:listen函数只是让套接字处于监听状态,并没有接收请求。接收请求需要使用accept函数。
listen函数的执行在TCP三次握手之前,而accept函数的执行则在TCP三次握手之后。
缓冲区的长度(能存放多少个client请求)可以通过 listen() 函数的 backlog参数指定,但究竟为多少并没有什么标准,可以根据你的需求来定,并发量小的话可以是10或者20。如果将 backlog的值设为SOMAXCONN,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。当请求队列满时,就不再接收新的请求,对于Linux,client会收到
ECONNREFUSED 错误。
2
3
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
3
struct sockaddr *cliaddr参数是取client的特定协议地址结构(一般由client填写)
socklen_t*addrlen参数指向一个局部整形变量,一般设置为(sizeof(struct sockaddr_in))
accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠(假定套接字为默认的阻塞方式)。
需要注意的一点是如果我们对返回客户协议地址不感兴趣,难么可以把cliaddr和addrlen均置为空指针,第二个参数为NULL,第三个也填写为NULL即可。
一个服务器通常仅仅创建一个监听套接字(sockfd),它在该服务器生命周期内一直存在。内核为每个由服务器进程接受的client连接创建一个已连接套接字。当server完成对某个给定的client的服务时,相应的已连接套接字就被关闭。
socket可以看成是用户进程与内核网络协议栈的编程接口,socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程通信。
这里所说的不同主机例如手机与PC的通信,为什么说手机与PC为不同主机?只是因为其外观不同吗?不是的,首先手机与PC的软件不同,其次是两者的硬件架构不同,手机采用的是ARM架构而PC大多是x86的架构(至于这两者架构有什么区别,博主现在还不了解,待后续补充,先自行查看参考资料)
下图为client(用户)与server(服务器)之间的通信过程。用到了socket(),connect(),bind(),listen(),accept(),write(),read()等函数。
socket基础之client与server之间的通信过程
首先说说什么是字节序,字节序分为大端字节序和小端字节序。大端字节序:最高有效位存储于最低内存地址处,最低有效位存储于最高内存地址。
小端字节序:最高有效位存储于最高内存地址处,最低有效位存储于最低内存地址。
主机字节序:不同的主机有不同的字节序。
(1)x86硬件架构下的主机为小端字节序
(2)Motorola 68000为大端字节序
(3)ARM架构的主机字节序是可配置的
网络字节序:网络字节序规定为大端字节序。
异构系统传输规定将主机A的字节序转化为网络字节序,再将网络字节序转化为主机B的字节序。(重要)(这里所说的异构系统可以理解为不同架构的系统,上面说的主机字节序因为架构不同,所产生的字节序也不同,所以规定统一先转化为网络字节序)
字节序之间的转换要用到字节序转化函数:
#include <netinet/in.h> uint16_t htons(uint16_thost16bitvalue); uint32_t htonl(uint32_thost32bitvalue);1
2
3
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
3
上面这两种均返回网络字节序的值
uint16_t ntohs(uint16_tnet16bitvalue); uint32_t ntohl(uint32_tnet32bitvalue);1
2
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
上面这两种均返回主机字节序的值
h代表host n代表network s代表short l代表long
这些函数后续博客都会详细说明
对字节序有一定了解后我们来说socket套接字地址结构,定义在
#include <netinet/in.h >中。
IPv4套接字地址结构如下:
struct in_addr { in_addr_t s_addr; }; //存储32位IPv4网络字节序地址(所以要将例如127.0.0.1这样的点分十进制地址转换,用到inet_aton函数)1
2
3
4
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
3
4
struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero[8]; }; //sin_family写入AF_INET,表示IPv4的地址族 //sin_port存储端口号(端口号使用网络字节序,必须用到htons函数将16位TCP或UDP端口号转换成网络字节序),在linux下端口号范围是0-65535,同时0-1024端口号已经被 系统使用或保留 //sin_zero[8]字段值未曾使用,设置为0即可1
2
3
4
5
6
7
8
9
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
3
4
5
6
7
8
9
client/server使用socket()函数创建套接字:
#include < sys/socket.h > int socket ( int family, int type,int protocol ); //socket函数在成功时返回一个小的非负整数值,它与文件描述符类似,我们把它称为套接字描述符(socketdescriptor),简称sockfd。失败时则返回-1。1
2
3
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
3
socket函数参数解释:
int family参数指明协议族:AF_INET //IPv4协议 AF_INET6 //IPv6协议1
2
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
int type参数指明套接字类型:
SOCK_STREAM //字节流套接字(表示用TCP传输,可靠、双向、面向链接的比特流) SOCK_DGRAM //数据报套接字(表示用UDP传输,不可靠、无连接通信) SOCK_RAW //原始套接字1
2
3
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
3
intprotocol参数指明协议类型(也可设置为0,当设置为0时选择family和type组合来确定传输协议):
IPPROTO_TCP //TCP传输协议 IPPROTO_UDP //UDP传输协议1
2
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
server使用bind()函数绑定套接口:
#include <sys/socket.h> int bind (int sockfd, const structsockaddr *myaddr, socklen_t addrlen); // 若成功则返回0,若出错则返回-1。1
2
3
![](http://static.blog.csdn.net/images/save_snippets.png)
1
2
3
bind函数作用:将IP、端口命名给socket,让它具有远程通信的功能。
bind函数参数解释:
int sockfd参数是socket套接字创建后返回的套接字描述符const struct sockaddr*myaddr参数是取特定协议地址结构(例如IPv4的sockaddr_in)
socklen_t addrlen参数表示所指向的特定协议地址结构的结构体大小
一般情况下客户端没必要使用bind绑定socket,因为在发送数据报文的时候,内核会帮你填写好报文头部的源IP和源端口,但是需要在client中初始化服务器端的IP和端口,这样才能在connect之后将报文发送至目的IP和目的端口。bind函数一般用于服务器,服务器端的第二个结构体参数不用初始化,因为当服务器accept时,服务器端接受报文后会将报文头部的源IP和源端口写入服务器的structsockaddr_in结构体中。
server使用listen()函数监听socket套接字:
#include < sys/socket.h > int listen ( int sockfd, int backlog); //若成功则返回0,若出错则返回-1。1
2
3
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
3
listen函数参数解释:
int sockfd参数指的是需要进入监听状态的socket套接字返回的套接字描述符int backlog参数指的是请求队列的最大长度(这是一个幻数,自己定义的请求队列的大小是一个值,但实际上可能允许的最大队列大于自己定义的这个值,kernel2.2之前这个参数代表了未完成与完成连接的个数,kernel2.6之后为完成建立连接的最大值)
listen函数仅由TCP服务器调用,所谓监听是指当没有客户端(client)的connect函数请求时,套接字处于“睡眠”状态,只有当接收到client的connect请求时,socket套接字才被“唤醒”来响应请求。注意:listen函数只是让套接字处于监听状态,并没有接收请求。接收请求需要使用accept函数。
listen函数的执行在TCP三次握手之前,而accept函数的执行则在TCP三次握手之后。
int backlog参数设置请求队列的最大长度,那么什么是请求队列呢?
当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(RequestQueue)。缓冲区的长度(能存放多少个client请求)可以通过 listen() 函数的 backlog参数指定,但究竟为多少并没有什么标准,可以根据你的需求来定,并发量小的话可以是10或者20。如果将 backlog的值设为SOMAXCONN,就由系统来决定请求队列长度,这个值一般比较大,可能是几百,或者更多。当请求队列满时,就不再接收新的请求,对于Linux,client会收到
ECONNREFUSED 错误。
server使用accept函数来等待接收client的connect请求:
#include < sys/socket.h> int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); //若成功则返回新的非负描述符connfd,若出错则为-11
2
3
![](http://static.blog.csdn.net/images/save_snippets_01.png)
1
2
3
accept函数参数解释:
intsockfd参数为监听套接字描述符(由socket创建,随后用作bind,listen的第一个参数)struct sockaddr *cliaddr参数是取client的特定协议地址结构(一般由client填写)
socklen_t*addrlen参数指向一个局部整形变量,一般设置为(sizeof(struct sockaddr_in))
accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠(假定套接字为默认的阻塞方式)。
需要注意的一点是如果我们对返回客户协议地址不感兴趣,难么可以把cliaddr和addrlen均置为空指针,第二个参数为NULL,第三个也填写为NULL即可。
一个服务器通常仅仅创建一个监听套接字(sockfd),它在该服务器生命周期内一直存在。内核为每个由服务器进程接受的client连接创建一个已连接套接字。当server完成对某个给定的client的服务时,相应的已连接套接字就被关闭。
相关文章推荐
- socket基础之c/s通信过程
- Linux基础之tcp socket 简单通信例程
- socket通信基础
- Socket通信编程基础__实例一__server端
- SOcket通信过程
- socket通信基础(三)
- [摘抄-Socket-学习中]UDP通信基础代码-server端
- TCP/IP详解(卷2实现)学习笔记(一)udp的socket通信过程底层实现概述(1)
- Socket通信基础介绍及例子
- python socket基础通信
- API SOCKET基础(一) TCP建立连接并通信
- socket 的通信过程
- Linux socket编程入门及客户端服务器端通信实现 – 基础篇
- 【原创】《Linux高级程序设计》杨宗德著 - Linux Socket网络编程基础 - BSD Socket网络通信编程
- Python网络通信之socket模块(四)基于Tcp/Ip的TCP交互通信serve/client的编写过程
- socket通信基础(二)
- 网络基础(2)--通信过程之一HTTP协议
- Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程
- Socket通信基础——基于TCP的用户登录
- 基于TCP的socket通信过程及例子