使用套接字创建连接 --- TCP


一.服务端的连接准备


1.创建套接字

int socket(int domain, int type, int protocol)


domain : PF_INET, PF_INET6以及PF_LOCAL等,表示什么类型的套接字

type:
    .SOCK_STREAM:表示字节流,TCP
    .SOCK_DGRAM :表示数据报,UDP
    .SOCK_RAW      :表示原始套接字

protoc:原本是用来指定通信协议,但现在基本废弃,因为协议已经通过前面两个参数指定完成,一般置0;


2.bind绑定电话号码


创建出来的套接字如果需要被别人使用,就需要调用bind函数把套接字和套接字地址绑定,就像去电信局登记我们的电话号码一样;

int bind(int fd, sockaddr *addr, socklen_t len);

需要注意第二个参数是通用地址格式:sockaddr * addr。 这里虽然接收的是通用地址格式,实际上传入的参数可能是ipv4,ipv6或本地套接字格式。
bind函数会根据len字段判断传入的参数addr该怎么解析,len字段表示的就是传入的地址长度,它是一个可变值。
这样,可以把它理解成

int bind(int fd, void* addr, socklen_t len);

那之前为什么不直接使用void呢?原因是void后面才定义的啦,BSD设计套接字的时候大约在1982年,那个时候还没有void *的支持。
所以每次使用的的时候,都需要将ipv4,ipv6或本地套接字格式转为通用套接字格式,内部根据len对地址进行解析和判断。

struct sockadd_in addr;
bind(fd, (struct sockaddr *)&addr, sizeof(addr));

bind监听设置通配地址,不挑活,只要有来,我就接。
对于ipv4,使用INADDR_ANY来完成设置
对于ipv6,使用IN6ADDR_ANY来完成设置

struct sockaddr_in addr;
addr.sin_addr.s_addr = htonl(INADDR_ANY);

当然也可以把端口设置为0,就相当于把端口的选择权交给操作系统内核来处理,操作系统内核会根据一定的算法选择一个空闲的端口,完成套接字的绑定。


3.listen:接上电话线,一切准备就绪


bind只是让我们的套接字和地址关联,如同登记电话号码。如果别人要打通电话,还需要把电话设备接入电话线,让服务器真正处于可接听的状态,这个过程需要依赖listen函数。

int listen(int socketfd, int backlog);


backlog:未完成链接队列的大小, 这个参数决定了可以接收的并发数目。但参数过大也会占用过多的系统资源,

4.accept:电话铃响起了


服务器收到客户端的连接请求了,应答成功,连接建立,这时候操作系统内核需要把这个事件通知到应用程序,并让应用程序感知到这个连接。相当于拿起电话筒开始交流了。

int accept(int listenfd, struct sockaddr *cliadd, sockelen_t *addrlen);

listenfd是前面通过bind,listen一系列操作而得到的套接字。
clidd:是通过指针方式获取的客户端的地址,addrlen告诉我们地址的大小,这里理解成当我们拿起电话机时,看到了来电显示,知道了对方的号码;
函数的返回值是一个全新的描述符,代表了与客户端的连接。


二.客户端发起连接的过程


第一步还是和服务端一样要先建立一个套接字。
不一样的是客户端需要调用connect向服务端发起请求。

1.connect:拨打电话

 

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);


第一个参数是连接套接字,第二三个参数是servaddr和addlen分别代表指向套接字地址结构的指针和该结构的大小。套接字地址结构必须含有服务器的ip和port;

在调用connect前不必非得调用bind函数,因为如果需要的话,内核会确定源ip地址,并按照一定的算法选择一个临时端口作为源端口。

如果是tcp套接字,那么connect会激发tcp的三次握手,而且仅在连接建立成功或出错时才返回。其中出错返回可能有一下几种情况:
    1).三次握手无法建立,客户端发出的syn包没有响应,于是返回了Timeout错误。这比较常见的原因是服务器的信息填错;
    2).客户端收到了RST(复位)回答,这时候客户端会立即返回CONNECTION REFUSED错误。比较常见于客户端发送连接请求时的请求端口写错,因为RST是TCP在发生错误时发送的TCP分节。
      产生RST的三个条件:目的地为某端口的SYN到达,然而该端口上没有正在监听的服务器;TCP想取消一个已有连接;TCP接收到一个根本不存在的连接上的分节。
    3).客户发出SYN包在网络上引起"destination unreachable",即目的地不可达的错误。可能是客户端和服务器的路由不通。

三.TCP三次握手的解读


1.客户端的协议栈向服务器端发送了 SYN 包,并告诉服务器端当前发送序列号 j,客户端进入 SYNC_SENT 状态;
2.服务器端的协议栈收到这个包之后,和客户端进行 ACK 应答,应答的值为 j+1,表示对 SYN 包 j 的确认,同时服务器也发送一个 SYN 包,告诉客户端当前我的发送序列号为 k,服务器端进入 SYNC_RCVD 状态;
3.客户端协议栈收到 ACK 之后,使得应用程序从 connect 调用返回,表示客户端到服务器端的单向连接建立成功,客户端的状态为 ESTABLISHED,同时客户端协议栈也会对服务器端的 SYN 包进行应答,应答数据为 k+1;
4.应答包到达服务器端后,服务器端协议栈使得 accept 阻塞调用返回,这个时候服务器端到客户端的单向连接也建立成功,服务器端也进入 ESTABLISHED 状态。

形象一点的比喻是这样的,有 A 和 B 想进行通话:
        A 先对 B 说:“喂,你在么?我在的,我的口令是 j。”
        B 收到之后大声回答:“我收到你的口令 j 并准备好了,你准备好了吗?我的口令是 k。”
        A 收到之后也大声回答:“我收到你的口令 k 并准备好了,我们开始吧。”
可以看到,这样的应答过程总共进行了三次,这就是 TCP 连接建立之所以被叫为“三次握手”的原因了。

四.总结


服务器端通过创建 socket,bind,listen 完成初始化,通过 accept 完成连接的建立。
客户端通过创建 socket,connect 发起连接建立请求。

代码实例可以参看:tcp通信实例

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值