socket不能bind请求的地址_TCP之socket 编程

socket的理解

1.socket本身有“插座“的意思,因此用来描述网络连接的一对一关系。

2.在TCP/IP协议中,“IP地址+TCP/UDP端口号”唯一标识网络通信中的一个进程,“IP地址+端口号”就称为socket。

3.在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socketpair 就唯一标志一个连接。

49bf26ca-9931-4099-b8e4-33e066088d55

网络数据流

网络数据流有大端和小端之分。发送主机通常将发送缓冲区中的数据按内存地址从高到低的顺序 发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存。因此 ,网络数据流的地址应这样规定:先发出的数据都是低地址,后发出的数据是高地址。

TCP/IP协议规定:网络数据流应采用大端字节序,即低地址高字节。

如果发送主机是小端字节序的,则需要做相应的字节序的转换。为使网络程序具有可移植性,使代码在大端和小端计算机上都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

cd986881569c40a4ad220406fd3e38b0

其中h表示host,n表示network,l表示32位长整数,s表示16位短整数。例如:htonl表示将32位的长整数从主机字节序转换为网络字节序。

TCP协议通讯流程

服务器:调用socket(),bind(),listen()完成初始化以后,调用accept()阻塞等待,处于监听端口的状态。

客户端:调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。

数据传输的过程:建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从accept()返回后立即调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发送给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read() 返回,发送下一条请求,如此循环下去。

如果客户端没有更多的请求,就调用close()关闭连接,就如同写端关闭的管道一样,服务器的read()返回0,服务器就知道客户端关闭了连接,也调用close()关闭连接。注意:?任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态,仍可接受对方发送来的 数据。

TCP协议使用的相关函数

1.socket的创建

(1)函数原型:

d0cb924492b3405693fd9533e0eb831e

(2)参数说明:

domain为协议域,对于IPv4,family指定参数为AF_INET。

type 为指定socket的类型,对于TCP协议,type的参数指定为SOCK_STREAM,表示面向流的传输协议;如果是UDP协议,则type的参数指定为SOCK_DGRAM,表示面向数据报的传输协议。

protocal参数为指定协议,一般指定为0即可。

(3)返回值

188c5ae99d90488daf41134a4241a704

2.bind():服务器通过调用bind()绑定一个固定的网络地址和端口号,客户端程序在得知服务器程序的地址和端口号后就可以向服务器发起连接。

(1)函数原型:

34de821278b1468797b6bfe23ccb19c4

(2)参数说明:

sockfd:套接字描述符。

addr:是一个sockaddr结构指针。

addr_len:指定addr的长度。

(3)bind()函数的返回值:

3deb228110894936a76eea95372f958b

bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络的文件描述符监听myaddr所描述的地址和端口号。struct socksddr* 是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度。对myaddr参数的初始化如下:

bzero(&servaddr,sizeof(servaddr));

servaddr.sin_family=AF_INET;

servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

servaddr.sin_port=htons(SERV_PORT);

首先将整个 结构体清零,然后设置地址类型为AF_INET,网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设置可以在所有的IP地址上监听,直到与某个客户建立了连接时才确定到底用哪个IP地址。

注意:我们常用的IP地址是用点分十进制表示的,因此要进行转换。inet_addr()函数用于把一个点分十进制的IP转换成一个长整型数。

函数原型如下:

60b8853ba5a34845bee01d92b332b0b6

3.listen函数

(1)函数原型

945165f7ac0f42248fdfd8e8036ff778

(2)参数说明

sockfd:套接字描述符。

backlog:最多允许backlog个客户端处于连接等待状态。一般为5——10个。

(3)返回值

03821ba060ca44c0ae5ced506eb2016a

4.accept函数

(1)函数原型

9e5ed116b9614a0fb388a769be9b888c

(2)函数说明

三方握手完成后,服务器调用accept()接受连接。如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户连接上来。accept()返回时传出客户端的地址和端口号。addrlen参数是一个传入传出型参数,传入的是调用者提供的缓冲区cliaddr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度。

(3) 返回值

b13e285ca26d48bbb960f8813c97e89e

5.客户端使用connect函数

(1)函数原型

3e99a29c76e847b3aa5fd28398d7c58b

(2)函数说明

由于客户端不需要固定的端口号,因此没有必要调用bind(),客户端的端口号由内核自动分配。

服务器也不是必须要调用bind(),但如果服务器不调用bind(),内核会自动给服务器分配监听端口,每次启动服务器时端口号都不一样,客户端要连接服务器时 就会遇到麻烦。

客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。

(3)返回值

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值