在网络编程------UDP协议网络程序一文中编写了根据传输层的UDP协议来进行服务器与客户端通信的代码,并介绍了相关的概念。在本文中将编写基于TCP协议的服务器与客户端通信代码。并对比TCP与UDP协议之间的差别。下面先介绍TCP协议。
TCP协议
TCP协议与UDP一样,都是基于传输层的协议。两个网络进程根据TCP协议在进行通信时,首先要相互建立连接。待双方确认连接建立成功之后,才可进行通信(UDP协议不用建立连接,直接进行通信)。这样做可以确保数据传送的可靠性,但同时因为建立连接等需要花费时间和资源,因此速度相对UDP会相对较慢。与UDP面向数据报传送方式的不同,TCP是面向字节流进行传送的,即发送方发送一定字节的数据,接收方可以以任意的长度接收。比如发送方一次发送了20字节的数据,接收方可以一次接受1个,2个字节等。而UDP协议要求发送方一次发送多少,接收方一次就必须接收多少。
因此,TCP协议具有以下特点:
(1)是传输层的协议
(2)面向连接,速度相对TCP会较慢,成本会相对较高
(3)保证可靠性
(4)面向字节流
下面基于TCP协议的简单网络程序。
在编写代码之前还要再认识一些接口函数。(部分接口函数已在网络编程------UDP协议网络程序中给出)
接口函数
1. 地址转换函数
基于TCP协议的网络编程,也要通过网络来进行通信,因此与UDP相同也要对IP地址进行相应的转换。
在UDP中介绍了IPv4类型的IP地址格式转换的两个函数,下面在介绍几个相关函数:
“点分十进制”转换为整型地址
int inet_aton(const char *cp, struct in_addr *inp);//头文件:<sys/socket.h> <netinet/in.h> <arpa/inet.h>
该函数与inet_addr函数一样,都只适用于IPv4类型的IP地址。
参数:
cp:需转换的“点分十进制”字符串类型的IP地址
inp:转换后的整型地址,该整型地址被封装在结构体struct in_addr中。所以inp是一输出型参数
返回值:成功返回0,失败返回-1。
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
该函数具有通用性,它适用于任意套接字类型的IP地址,具体是转换哪种地址,由参数给出。
参数:
af:套接字类型的地址标识,即struct sockaddr结构体的第一个成员。如对于IPv4类型的IP地址,该参数为AF_INET。
src:需转换的“点分十进制”字符串类型的IP地址
dst:该参数指向转换后的整型地址。因此该参数也是一输出参数。
返回值:成功返回0,失败返回-1。
整型地址转换为“点分十进制”字符串
在UDP中介绍过的inet_ntoa函数为:
char *inet_ntoa(struct in_addr in);
在转换时该函数内部为我们在静态存储区申请了一片内存存放转换后的字符串,然后将这片内存的地址以返回值的形式带回。所以,不需要我们手动释放。
但是,当多次调用该函数时,后面调用的结果会覆盖前面的结果。也就是说每次调用转换后的结果都放在同一内存中。因此,在多线程环境中调用该函数时,这片区域就相当于临界资源,多个线程都可进行访问。因此可能会出现异常。所以该函数不是线程安全函数。
所以,可以调用以下函数,来由我们自己提供存放字符串的内存,同时该函数适用于任意类型的IP地址转换:
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
参数:
af:16位的IP地址类型标识,如AF_INET
src:指向要转换的变量
dst:存放转换后的“点分十进制”字符串,因此它是一个输出型参数
size:dst的字节长度
返回值:成功返回0,失败返回-1
2. listen函数
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
函数功能:该函数用于客户端,使sockfd处于监听状态,处于监听状态的网卡文件才能接受客户端发来的连接请求。
参数:
sockfd:socket函数返回的文件描述符
backlog:等待队列的中等待连接的个数。
返回值:成功返回0,失败返回-1.
注意:
当系统中的资源不足以支持与客户端进行连接并提供服务时,此时就要是请求的连接处于等待队列中。待系统中有多余的资源时,在进行连接。
为保证服务器一直处于忙碌状态,就必须维护一个等待队列。因为如果不维护等待队列,当服务器资源不足时时,客户端发来的连接请求就会被忽略,当服务器空闲下来时,可能没有连接请求发来,此时,服务器就可能处于空闲状态,而使资源得不到利用。所以,必须维护一个等待队列。
同时,这个等待队列不能太长。因为等待队列的维护是需要消耗资源的。应将更多的资源用于服务上,所以一般将等待队列的长度设置为5,当等待队列中的请求连接数超过5时,就直接忽略多余的连接。
3. connect函数
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
函数功能:客户端通过调用该函数向服务器端发起连接请求
参数:
sockfd:客户端程序中由socket函数返回的文件描述符
addr:客户端要连接的服务器端的存放套接字相关信息的