1、一、 TCP/IP、UDP 的基本概念TCP/IP 即传输控制协议,是一个工业标准协议集,为广域网设计的。涉及多个其他协议,主要以TCP 和 IP 为代表。UDP 即用户数据报协议。TCP/IP 进行数据传输主要分为两个过程:建立连接过程和数据传输过程。TCP/IP 协议通过三次握手建立一个可靠的连接,步骤: 第一次握手:建立连接时,客户端发送 syn 包(syn=j) 到服务器,并进入 SYN_SEND 状态,等待服务器确认;第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=j+1) ,同时自己也发送一个 SYN包(syn=k ) ,即 SYN+ACK 包,此时服务器进入
2、SYN_RECV 状态;第三次握手:客户端收到服务器的 SYNACK 包,向服务器发送确认包 ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。数据传输过程:发送端发送数据,然后进入等待 ACK 确定信号状态,随后接收端接收到数据,发送 ACK 确认信号,发送端接收到 ACK 后才发送下一组数据,同时发送端有一个定时器,定时时间到了没有接收到 ACK,就认为发送失败了,进行重新发送。因为发送端发送完数据后处于等待状态,因此为了提高效率,引入“滑动窗口”的概念,就是发送的时候一次发送多组数据,相当于窗口的大小,然后当接收到第一个 ACK 后,
3、就将窗口向后移动一个数据,就形成了滑动窗口的情况。二、Socket 编程1 创建套接口:Int socket(int family,int type,int ptotocol)功能:生成一个套接口描述返回值:成功返回套接口描述符,失败返回-1.参数 1:family 指明协议族PF_UNIX-UNIX 协议族PF_INET-IPV4 协议PF_INET6-IPV6 协议AF_ROUTE路由套接口参数 2:type 指明通信字节流,取值SOCK_STREM-TCP 方式SOCK_DGRAM-UDP 方式SOCK_RAW-原始套接口参数 3:ptotocol 一般设置为 02 绑定端口:Int b
4、ind(int sockfd,const struct sockaddr * my_addr,socklen addrlen)功能:调用 socket 创建一个套接口后,需要使用 bind 函数在这个套接口上绑定一个指定的端口号和 IP 地址。返回:成功 0,失败-1参数 1:sockfd 已建立的 socket 编号(描述符)参数 2:my_addr 是一个指向 sockaddr 结构体类型的指针参数 3:addrlen 表示 my_addr 结构的长度,可以用 sizeof 获得3 等待监听函数Int listen(int sockfd,int backlog) 功能:监听是指 socke
5、t 的端口一直处于等待的状态,监听网络中的所有客户机,耐心等待某一客户机的发送请求。如果客户端有连接请求,端口就会接受这个链接。返回:成功 0,失败-1参数 1:sockfd 已建立的套接口描述符参数 2:backlog 能同时处理的最大连接请求数,如果超过这个数目,客户端将接收到ECONNREFUSED 拒绝连接的错误4 接受连接的函数Int accept(int sockfd,struct sockaddr *addr,socketlen *addrlen)功能:服务区处于监听状态,若某时获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时在处理客户
6、机的连接请求,接受连接请求的函数式 accept。返回:成功返回新的套接口描述符 ,失败-1参数 1:sockfd 表示处于监听状态的 socket参数 2:addr 是一个 sockaddr 结构体类型的指针,系统会把远程主机的信息保存到这个指针所指向的结构体中。参数 3:addrlen 表示 sockaddr 的内存长度,可以用 sizeof 函数取得。 当 accept 函数接受一个连接时,会返回一个新的 socket 标示符,以后的数据传输与读取就是通过这个新的 socket 编号来处理,原来参数中的 socket 也可以继续使用。接受链接以后,远程主机的地址和端口信息将会保存到 ad
7、dr 所指的结构体中。5 请求连接的函数Int connect(int sockfd,const struct sockaddr *serv_addr,int addrlen)功能:客户机向服务器发送信息之前,需要先发送一个链接请求,请求与服务器建立一个连接请求,请求与服务器建立 TCP 通信连接。Connect 函数会将本地的 socket 连接到 serv_addr 所指定的服务器 IP 与端口号上去。返回:成功 0,失败-1参数 1:sockfd 表示已建立的套接口标示符。参数 2:serv_addr 是一个结构体指针,指向一个 sockaddr 结构体,这个结构体存储着远程服务器的 I
8、P 与端口号信息参数 3:addrlen 表示 sockaddr 结构体的内存长度,可以用 sizeof 取得6 数据发送函数 TCP 使用 int send(int socket,const void*msg,int len,unsigned int flags ); 功能:不论是客户还是服务器应用程序都用 send 函数来向 TCP 连接的另一端发送数据。客户程序一般用 send 函数向服务器发送请求,而服务器则通常用 send 函数来向客户程序发送应答。返回:成功返回已发送的字符数,失败-1参数 1:socket 指定发送端套接字描述符;参数 2:msg 指明一个存放应用程序要发送数据的
9、缓冲区即需要发送数据的指针;参数 3:len 指明实际要发送的数据的字节数;参数 4:flags 一般置 0。这里只描述同步 Socket 的 send 函数的执行流程。当调用该函数时,send 先比较待发送数据的长度 len 和套接字 socket 的发送缓冲的长度,如果 len 大于 socket 的发送缓冲区的长度,该函数返回 SOCKET_ERROR;如果 len 小于或者等于 socket 的发送缓冲区的长度,那么 send 先检查协议 是否正在发送 socket 的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送 socket 的发送缓冲中的数据或者 sock
10、et 的发送缓冲中没有数据,那么 send就比较 socket 的发送缓冲区的剩余空间和 len,如果 len 大于剩余空间大小 send 就一直等待协议把 socket 的发送缓冲中的数据发送完,如果 len 小于剩余 空间大小 send 就仅仅把 buf 中的数据 copy 到剩余空间里(注意并不是 send 把 socket 的发送缓冲中的数据传到连接的另一端的,而是协议传的,send 仅仅是把 buf 中的数据 copy 到 socket 的发送缓冲区的剩余空间里) 。如果 send 函数 copy 数据成功,就返回实际 copy 的字节数,如果 send 在 copy 数据时出现错误
11、,那么 send 就返回 SOCKET_ERROR;如果 send 在等待协议传送数据时网络断开的话,那么send 函数也返回 SOCKET_ERROR。要注意 send 函数把 msg 中的数据成功 copy 到 socket 的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。注意:在 Unix 系统下,如果 send 在等待协议传送数据时网络断开的话,调用 send 的进程会接收到一个 SIGPIPE 信号,进程对该信号的默认处理是进程终止。7 数据接收函数 TCP 使用int recv( int socket,void *buf,int len,unsi
12、gned int flags); 功能:不论是客户还是服务器应用程序都用 recv 函数从 TCP 连接的另一端接收数据。实现接收远程主机发来的数据,并把这些数据保存在一个数组中。返回:成功返回接收到的字符数,失败-1参数 1:socket 指定接收端套接字描述符;参数 2:buf 指明一个指针数组,该数组用来存放 recv 函数接收到的数据;参数 3:len 指明 buf 数组的长度;参数 4:flags 一般置 0。这里只描述同步 Socket 的 recv 函数的执行流程。当应用程序调用 recv 函数时,recv 先等待socket 的发送缓冲中的数据被协议传送完毕,如果协议在传送 s
13、ocket 的发送缓冲中的数据时出现网络错误,那么 recv 函数返回 SOCKET_ERROR,如果 socket 的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv 先检查套接字 socket 的接收缓冲区,如果 socket 接收缓冲区中没有数据或者协议正在接收数据,那么 recv 就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv 函数就把 socket 的接收缓冲中的数据 copy 到 buf 中(注意协议接收到的数据可能大于 buf 的长度,所以 在这种情况下要调用几次 recv 函数才能把 socket 的接收缓冲中的数据 copy 完。recv 函数仅仅
14、是 copy 数据,真正的接收数据是协议来完成的) ,recv 函数返回其实际 copy 的字节数。如果 recv 在 copy 时出错,那么它返回 SOCKET_ERROR;如果 recv 函数在等待协议接收数据时网络中断了,那么它返回 0。8 write 与 read 函数ssize_t write (int fd, const void *buf, size_t count)ssize_t read (int fd, void *buf, size_t count)功能:write 函数用来向文件中写入数据,read 函数用来从文件中读取数据。在网络编程中,当 socket 建立后,向这
15、个 socket 种写入数据即表示向远程主机传送数据,从 socket 读取数据则相当于接收远程主机传过来的数据。二者的作用分别与 send 和 recv 函数类似。返回:成功返回已写入或已读出的字节数,失败-1参数 1:表示已建立的 socket参数 2:buf 指向一段内存的指针参数 3:count 表示 buf 指向内存的长度8 获得和设置当前套接字选项特定属性的值Int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t*optlen)Int setsockopt(int sockfd,int level
16、,int optname,const void *optval,socklen_t optlen)返回:0 成功,-1 失败参数 1:sockfd 套接字描述符参数 2:level 套接字选项的协议层 level参数 3:optname 套接字选项条目名称参数 4:optval 套接字选项接收缓冲区的指针参数 5:选项的输入缓冲区长度及返回的选项缓冲区长度指针 optlen 和*optlen协议层参数 level 代表套接字选项所在的协议层次。9 I/O 多路复用I/O 多路复用应用场合:1) 客户端同时处理多个文件描述符2) 客户端要同时处理多个网络套接字3) TCP 服务器同时处理监听套接字和连接套接字4) 服务器同时处理 TCP 和 UDPI/O 多路复用时,应用程序在执行 Select 函数系统调用的时候被阻塞,而不是被阻塞在实际的 I/O 系统调用阶段。Select 函数允许进程指示多个事件中的一个或多个的发生,或者若在规定的时间内没有任何事件发生,内核唤醒应用进程进行后续处理。