【UNIX网络编程卷1】第1章 简介

1.1 概述

要编写通过计算机网络通信的程序,首先要确定这些程序相互通信所用的协议。
一般认为Web服务器程序是一个长时间运行的程序(守护程序,daemon)它只在响应来自网络的请求时才发送网络消息。协议的另一端是Web客户程序,如某种浏览器,与服务器进程的通信总是由客户进程发起。
在设计网络应用时,确定总是由客户发起请求往往能够简化协议和程序本身。

1.2 一个简单的时间获取客户程序

下例是TCP当前时间查询客户程序的一个实现。该客户与其服务器建立一个TCP连接后,服务器以直观可读格式简单地送回当前时间和日期:

#include "unp.h"

int main(int argc,char* argv[])
{
	int sockfd,n;
	char recvline[MAXLINE+1];
	struct sockaddr_in servaddr;
	
	if(argc!=2)
		err_quit("usage:a.out <IPaddress>");
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
		err_sys("socket error");
	
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(13);   /*daytime server*/
	
	if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0)
		err_quit("inet_pton error for %s",argv[1]);
	if(connect(sockfd,(SA*)&servaddr,sizeof(servaddr))<0)
		err_sys("connect error");
	while((n=read(sockfd,recvline,MAXLINE))>0)
	{
		recvline[n]=0;
		if(fputs(recvline,stdout)==EOF)
			err_sys("fputs error");
	}
	if(n<0)
		err_sys("read error");
	exit(0);
}

编译时需加上静态链接库,编译命令如下:

gcc daytimetcpcli.c -o daytimetcpcli -lunp 

程序运行结果如下:
在这里插入图片描述
输入百度的ip,结果如下:
在这里插入图片描述
程序说明:

  • socket函数创建了一个网际(AF_INET)字节流(SOCK_STREAM)套接字。该函数返回一个小整数描述符,以后的所有函数调用(如connectread)就用该描述符来标识这个套接字。
  • 把服务器的IP地址和端口号填入一个网际 套接字地址结构(名为servaddrsockaddr_in结构变量)。使用bzero()函数将整个结构清零后,置地址族为AF_INET,端口号为13(时间获取服务器的常用端口)。IP地址为第一个命令行参数的值。网际套接字地址结构中IP地址和端口号必须使用特定格式,因此调用库函数htons()(主机到网络短整数)去转换二进制端口号,又调用库函数inet_pton()(呈现形式到数值)去将ASCII命令行参数(IP地址)转换为合适的格式。
  • connect函数应用于一个TCP套接字时,将与由它的第二个参数指向的套接字地址结构指定的服务器建立一个TCP连接。该套接字地址结构的长度也必须作为该函数的第三个参数指定。
  • 使用read函数读取服务器的应答。由于分段传输,数据可能被分成多个包,因此通常需要将read编写在某个循环中,当read返回0(表示对端关闭连接)或负值(表明发生错误)时终止循环。

1.5 一个简单的时间获取服务器程序

编写一个简单的TCP时间获取服务器程序,与前文的客户程序一道工作:

#include "unp.h"
#include <time.h>

int main(int argc,char* argv[])
{
	int listenfd,connfd;
	struct sockaddr_in servaddr;
	char buff[MAXLINE];
	time_t ticks;
	
	listenfd=socket(AF_INET,SOCK_STREAM,0);
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	servaddr.sin_port=htons(13);
	
	Bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
	Listen(listenfd,LISTENQ);
	
	for(;;)
	{
		connfd=Accept(listenfd,(SA*)NULL,NULL);
		ticks=time(NULL);
		snprintf(buff,sizeof(buff),"%.24s\r\n",ctime(&ticks));
		Write(connfd,buff,strlen(buff));
		Close(connfd);
	}
}
程序说明
创建TCP套接字

11 TCP套接字的创建与客户程序相同

捆绑端口到套接字

12-17 填写一个网际套接字地址结构,调用bind函数,端口13被捆绑到所创建的套接字。指定IP地址为INADDR_ANY,如果服务器主机有多个网络接口,服务器进程就可以在任意网络接口上接受客户连接。

把套接字转换成监听套接字

18 调用listen函数将套接字转换为监听套接字。socketbindlisten这3个调用步骤是任何TCP服务器准备所谓的监听描述符(listenfd)的正常步骤。

接受客户连接,发送应答

20-25 通常情况下,服务器进程在accept调用中被投入睡眠,等待某个客户连接的到达并被内核接受。经过TCP三次握手后,accept返回,其返回值是一个称为已连接描述符的新描述符(connfd)。该描述符用于与新近连接的那个客户通信。accept为每个连接到本服务器的客户返回一个新描述符。

终止连接

26 服务器通过调用close关闭与客户的连接。该调用引发正常的TCP连接终止序列。

本服务器程序一次只能处理一个客户,如果多个客户连接差不多同时到达,系统内核在某个最大数目的限制下把它们排入队列,然后每次返回一个给accept函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值