网络编程总结(2)

socket

TCP模型客户端服务器模型
在这里插入图片描述
从上面的图来看,服务器首先是需要出去监听状态之后,客户端才能够进行TCP连接。首先服务器先初始化socket套接字。之后服务器执行bind函数,将本次套接字与一个ip地址和一个客户端知道的端口绑定。然后listen函数,将这个套接字转换成监听套接字。然后在accept函数中不断阻塞,直到有客户端来连接。
当有客户端连接的时候,就会发生面试必考的三次握手流程。

三次握手

在这里插入图片描述
首先,客户端处于closed状态,服务器处于listen状态。
第一次握手,客户端给服务器发送一个SYN报文,并指明了客户端的初始序列号ISN(c),此时,客户端处于SYN_send状态。
第二次握手,服务器收到客户端的SYN报文之后,也会发送自己的SYN报文,并指明了自己的ISN(s)。同时会把客户端的ISN+1作为ACK发送回客户端。此时,服务器处于SYN_recv状态。
第三次握手,客户端收到SYN报文之后,也会发送一个ACK,这个ACK是服务器的ISN+1值。此时客户端处于establised状态。

1、ISN+1作为ACK返回这是一种验证的方式,代表已经收到你上一次发送的数据包。
2、两次握手的话,服务器不知道客户端有没有收到数据的能力。四次握手的话就会浪费网络资源。

socket流程的函数

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

创建套接字。第一个参数是地址族,填的是这个是ipv4还是ipv6。PF_INET、PF_INET6这些。
第二个参数是套接字类型,SOCK_STREAM 代表TCP连接,SOCK_DGRAM代表UDP连接,SOCK_RAW代表原始套接字。
第三个参数默认为0,如果第二个参数不是一个的话,这个参数就会与之改变。

bind()函数
bind(int fd, sockaddr * addr, socklen_t len)

这个格式需要我们每次写socket的时候写强制转换,是因为这个出来的时候是82年,还没有C还没有void*的格式。

struct sockaddr_in addr;
bind(sock,(struct sockaddr*)&addr,sizeof(addr);

通用的结构体

/* 描述通用套接字地址  */
struct sockaddr
{
    sa_family_t sa_family;  /*地址族*/
    char sa_data[14];   /*地址值*/
};

常用的结构体

struct sockaddr_in
 
{ 
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/ 
unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/ 
struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/ 
unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/ 
};

注:这里的地址族和上面socket函数填的地址族不太一样,
这里是这样的AF_INET而不是PF_INET。第一个字母不同,但是去头文件看这两者的宏定义是一样的。

对于bind函数,写客户端会不知道网络地址的,而且不知道会在上面硬件上使用,所以我们一般使用通配地址。比如192.168.1.1和10.1.8.1这两个,会同时收到数据。

struct sockaddr_in addr;
addr.sin_addr.s_addr=htonl(INADDR_ANY); /* IPV4 通配地址 */

还有端口,如果把端口设置为0,就是相当于把端口的选择权交给内核处理,操作系统会根据算法选择一个空闲的端口,完成套接字的绑定,服务器不会这样做。

listen()函数
int listen(int sockerfd,int backlog);

初始化创建的套接字,可以认为是一个“主动套接字”,其目的主动发起请求,通过listen函数,可以将原来的这个主动套接字转换称为被动套接字,就是告知系统内核我这个套接字我想让它来等待用户请求的,然后内核就会做好接受用户请求的所有准备,就比如弄了两条队列。

第一个参数为套接字描述符,就是用来转化的。第二个参数为backlog,官方的解释是未完成连接队列的大小,这个参数的大小决定了可以接受的并发数目。

accept()函数
int accept(int listensockfd, struct sockaddr *cliaddr, socklen_t *addrlen)

第一个参数是listen套接字,这个函数有两个返回值,第一个是通过指针的方式获取客户端的地址,另一个是函数的返回值。
这种写法分离了数据,比较牛。

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

客户调用这个函数前不必一定调用bind函数,如需要内核会自己调用的,因为这个主动连接的一方,是端口是不用固定的。
第一个参数是套接字,第二个是地址结构体,第三个是长度。

这个函数只有在函数调用成功或者失败才会返回。
失败主要有几个原因:
1、服务端端口错了,连接拒绝。
2、第一次握手的SYN包没有答复,一般是ip写错。
3、没网。

以上就是socket的几个主要且重要的函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值