客户软件的设计算法和实现技术
现在开始进入进入客户-服务器编程的具体如何实现的环节了,这一板块更重要的是学习算法,而不是研究细节。掌握基本算法,掌握通信方式的选择策略,掌握使用套接字的技术
实现客户端有TCP和UDP两种算法
TCP客户端算法
-
找到期望与之通信的服务器的IP地址和协议端口号
第一步,要建立客户端和服务器的连接,肯定要找到服务器的ip地址
ip如果知道的话可以直接传递给程序,如果不知道ip地址,知道域名也行,先请求域名服务器,返回信息后拿到ip地址假设 域名:merlin.cs.purue.edu 根据它获得IP地址
struct hostent *hptr; char *examplenam=“merlin.cs.purdue.edu” if( hptr=gethostbyname( examplenam )){ /* IP address is now in hptr->h_addr */ }else{ /* error in name-handle it */ }
第二步,找端口号也是一样的,可以直接传递数字端口,也能更具协议端口的名字请求端口服务器,获取端口的具体数字
若某个TCP客户需要查找SMTP的正式协议端口号,它便调getservbyname,如下例:
struct servent *sptr; if (sptr=getservbyname(“smtp”,”tcp”)){ /* port number is now in sptr->s_port */ }else{ /* error occurred-handle it */ };
同时,连接的时候也要指定协议,协议也具有协议名
例如查找UDP的正式协议号,可以调用getprotobynamestruct protoent *pptr; if(pptr=getprotobuname(“udp”)){ /* official protocol number is now in pptr->p_proto */ }else{ /* error occurred-handle it */ }
-
分配套接字
在套接字被使用前,创建该套接字的应用程序必须用其他系统调用把套接字数据结构中的信息填上
-
指明此连接需要在本地机器中的、任意的、未使用的协议端口,并允许TCP选择一个这样的端口
为什么不用指定本地ip地址?因为路由器或多接口主机拥有多个IP地址,这就有可能在选择转发接口时,选择了一个并不匹配的地址。套接字允许应用程序将本地地址字段放置不填,而允许TCP/IP软件在客户与某个服务器进行连接时自动选取本地IP地址端口选择不冲突,非熟知的就行
-
将这个套接字连接到服务器
系统调用connect允许TCP套接字发起连接(完成三次握手)。连接成功返回0,否则返回1retcode = connect( s, remaddr, remaddrlen )
s是套接字描述符,remaddr是一个sockaddr_in类型的结构的地址,remaddrlen是第二个参数的长度
-
使用应用级协议与服务器通信(在此,往往包含发送请求和等待应答)
bptr = buf; buflen = BLEN; /* send request */ send( s, req, strlen( req), 0); /* read response (may come in many pieces) */ while ( ( n = recv( s, bptr, buflen, 0 ) )>0 ){ bptr += n; buflen -=n; }
客户端重复调用recv是必须的,因为,TCP不是面向块的(block-oriented)而是面向流的(stream-oriented)协议:它保证传递发送者所发出的字节序列,但是并不保证按照这些字节所写入时的组传送。另一途径是,TCP可能在发送报文段之前,要在其缓存中积累许多的字节(例如,为了填满一个数据报)
客户端接收数据的不确定性是TCP编程的一个基本概念
-
关闭连接
客户在发送最后一个请求后,发起部分(发送请求)关闭;服务器在发送完最后一个响应后在关闭整个连接。
2022/3/14更新
UDP客户端编程
UDP有两种模式:连接的、非连接的
- 使用连接模式,客户使用connect调用指明远程端点的地址。不用每次重复指明远程地址就可以发送和接受报文
- 使用非连接的模式,客户并不把套接字连接到一个指定远程端点上,而是在每次发送报文时指明远程目的地
过程:
- 找到期望与之通信的服务器的IP地址和协议端口号分配套接字
- 指明这种通信需要本地机器中的、任意的、未使用的协议端口,并允许UDP选择一个这样的端口
- 指明报文所要发往的服务器
- 使用应用级协议与服务器通信(在此,往往包含发送请求和等待应答)
- 终止通信,删除套接字
UDP编程算法和TCP类似,与TCP不同的是,UDP提供了报文传送。客户每次到用send,UDP便向服务器发送一个报文,每次调用recv都返回一个完整的报文
如果要在internet中很好的工作,UDP的客户必须实现超时和重传来保证可靠性
过程库
为使编程时间尽量减少,程序员可以一次编写代码,将其置于某个过程之中,然后,只是简单地在各个客户程序中调用这个过程就行了
比如说:
int connectsock(const char *host, const char *service, const char *transport);
int connectTCP(const char *host, const char *service)
{
return connectsock( host, service, “tcp”);
}
int connetUDP(const char *host, const char *service)
{
return connectsock( host, service, “udp”);
}
具体的编程应用练习在下一篇文章实现:http://www.liangyuanshao.top/blog/article/29