tcp服务端流程:
1、建立socket( socket 函数)
2、设置服务端sockaddr_in结构体,并绑定到第一步的socket(bind函数)
3、设置客户端信息队列长度,也就是已经建立连接的客户端和未连接上客户端的最大数量,并把socket设为侦听状态(listen函数)
4、等待客户端连接,并得到与该客户端绑定的socket(accept函数)
5、收发数据(send、recv函数)
6、关闭绑定客户端的socket,关闭侦听socket(close函数)
tcp客户端流程:
1、创建socket
2、设置要连接的服务器ip地址和端口到sockaddr_in结构体
3、连接服务器(connect函数)
4、收发数据
5、关闭客户端socket
tcp连接时的三次握手:
客户端connect时发出syn信号给服务端,服务端收到后返回ack信号和syn信号,客户端收到后发送ack信号给服务端。
可以用两个人在线语音形象的描述三次握手
我对你说:能听到我说话吗(syn)?你听到后跟我说:能听到(ack),那你能听到我说话吗(syn)?我听到后跟你说:能听到(ack)。这就确保了信号在你和我之间的双向连通
tcp断开时的四次握手:
A端发出fin信号,B端收到后返回ack信号,B端再发fin信号,A端收到后返回ack信号。这里的fin只是代表单边结束,因为tcp是全双工,所以必须两端都断开才断开。断开之后在read会得到一个数据长度0的包,可以用来处理断开之后的事情
细节:
主要是网络字节序的问题,需要把sockaddr_in中的ip和port转换为网络字节序也就是大端模式,例如:htons(port_number),在sockaddr_in中的端口号是一个short类型,因此是htons(host to net short)。
转换ip的函数有inet_addr、inet_aton、inet_pton,其中inet_pton比较新,ipv6也可以用,具体使用方法看man手册。
UDP服务端:
1、创建socket
2、设置服务端sockaddr_in结构体,并绑定到第一步的socket(bind函数)
3、等待客户端连接,获得客户端sockaddr结构体从而知道ip和端口
4、收发数据(recvfrom、sendto函数)
5、关闭服务端socket
UDP客户端
1、创建socket
2、设置要连接的服务器ip地址和端口到sockaddr_in结构体
3、收发数据
4、关闭客户端socket
细节:
除了网络字节序最主要的点就是recvfrom这个函数出错返回后perror打印出的的invalid argument错误,原因是ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen)这个函数里面的addrlen在使用之前必须先初始化为sockaddr结构体的长度,函数执行之后会变成src_addr的真实长 度。如果不初始化,程序能不能执行全凭运气...
man recvfrom中关于addrlen参数的说明:
If src_addr is not NULL, and the underlying protocol provides the
source address, this source address is filled in. When src_addr is
NULL, nothing is filled in; in this case, addrlen is not used, and
should also be NULL. The argument addrlen is a value-result argument,
which the caller should initialize before the call to the size of the
buffer associated with src_addr, and modified on return to indicate the
actual size of the source address. The returned address is truncated
if the buffer provided is too small; in this case, addrlen will return
a value greater than was supplied to the call.