880 likes | 1.05k Views
网络五子棋. Described by 张卯安. 过程. 从程序员的角度来看: 首先有主机创建网络连接,等待客机响应,在这个等待的过程中,主机随时可以取消连接;接收到客户机响应之后,就可以开始走棋,双方每下完一步棋,就必须发送相关的网络数据给对方,同时更新棋盘,达到同步,之后就要等待对方下棋,并且准备接收对方发送过来的数据. 功能分析. 能够创建网络主机。在界面上添加功能按钮,显示创建主机对话框,对话框显示主机的 IP ,还有取消按钮(在连接过程中可以随时结束) 游戏客户端能够加入主机。添加软键盘,用来输入主机 IP
E N D
网络五子棋 Describedby 张卯安
过程 • 从程序员的角度来看: 首先有主机创建网络连接,等待客机响应,在这个等待的过程中,主机随时可以取消连接;接收到客户机响应之后,就可以开始走棋,双方每下完一步棋,就必须发送相关的网络数据给对方,同时更新棋盘,达到同步,之后就要等待对方下棋,并且准备接收对方发送过来的数据
功能分析 • 能够创建网络主机。在界面上添加功能按钮,显示创建主机对话框,对话框显示主机的IP,还有取消按钮(在连接过程中可以随时结束) • 游戏客户端能够加入主机。添加软键盘,用来输入主机IP • 棋子坐标的网络传输。传递报文,表示最新落子的坐标,另一端接收报文,并且刷新棋盘 • 界面标记游戏状态。显示当前落子方,如果哪方赢了,弹出对话框提示
主要涉及点 • 通过对网络五子棋的功能分析,可以看出: 1、网络是必不可少 2、等待网络数据的时候,保证程序不进入死锁 3、依赖窗口部件
TCP/IP参考模型 OSI参考模型 应用层 应用层 表示层 会话层 传输层 传输层 网络层 网络层 数据链路层 网络接口层 物理层 TCP/IP协议 • TCP/IP协议概述
TCP/IP协议族 • TCP/IP 实际上一个一起工作的通信家族,为网际数据通信提供通路。为讨论方便可将TCP/IP 协议组大体上分为三部分: 1.Internet 协议(IP) 2.传输控制协议(TCP)和用户数据报文协议(UDP) 3.处于TCP 和UDP 之上的一组协议专门开发的应用程序。它们包括:TELNET,文件传送协议(FTP),域名服务(DNS)和简单的邮件传送程序(SMTP)等许多协议。
网络层 • 第一部分也称为网络层。包括Internet 协议(IP)、网际控制报文协议(ICMP)和地址识别协议(ARP)。 • Internet 协议(IP) 该协议被设计成互联分组交换通信网,以形成一个网际通信环境。它负责在源主机和目的地主机之间传输来自其较高层软件的称为数据报文的数据块,它在源和目的地之间提供非连接型传递服务。
网际控制报文协议(ICMP) 它实际上不是IP层部分,但直接同IP层一起工作,报告网络上的某些出错情况。允许网际路由器传输差错信息或测试报文。 • 地址识别协议(ARP) ARP 实际上不是网络层部分,它处于IP和数据链路层之间,它是在32位IP地址和48位局域网物理地址之间执行翻译的协议。
传输层协议 • 第二部分是传输层协议。包括传输控制协议和用户数据报文协议。 由于IP 提供非连接型传递服务,因此TCP应为应用程序存取网络创造了条件,使用可靠的面向连接的传输层服务。该协议为建立网际上用户进程之间的对话负责。此外,还确保两个以上进程之间的可靠通信。它所提供的功能如下。 1.监听输入对话建立请求。 2.请求另一网络站点对话。 3.可靠的发送和接收数据。 4.适度的关闭对话。
用户数据报文协议(UDP) • UDP 提供不可靠的非连接型传输层服务,它允许在源和目的地站点之间传送数据,而不必在传送数据之前建立对话。该协议还不使用TCP使用的端对端差错校验。当使用UDP时,传输层功能全都发挥,而开销却比较低。它主要用于那些不要求TCP协议的非连接型的应用程序。例如,名字服务、网络管理、视频点播和网络会议等。
应用程序部分 • 最后是应用程序部分。这部分包括Telnet,文件传送协议(FTP 和TFTP),简单的文件传送协议(SMTP)和域名服务(DNS)等协议。 • TCP/IP 使用了主干网络,能连接各种主机和LAN 的多级分层结构,局部用户能方便的联网,不致影响到整个网络系统。此外这种结构还有利于局部用户控制操作和管理。 • TCP/IP 具有两个主要功能。第一是IP在网络之间(有时在个别网络内部)提供路由选择。第二是TCP将TP传递的数据传送的接收主机那的适当的处理部件。
Internet 协议(IP) • IP主要有以下四个主要功能: (1)数据传送 (2)寻址 (3)路由选择 (4)数据报文的分段
IP功能 • IP的主要目的是为数据输入/输出网络提供基本算法,为高层协议提供无连接的传送服务。这意味着在IP将数据递交给接收站点以前不在传输站点和接收站点之间建立对话(虚拟链路)。它只是封装和传递数据,但不向发送者或接收者报告包的状态,不处理所遇到的故障。 • IP协议不注意包内的数据类型,它所知道的一切是必须将某些称为IP 帧头的控制协议加到高层协议(TCP 或者UDP)所接受的数据上。
传输控制协议(TCP) • TCP(传输控制协议Transmission Control Protocol)是重要的传输层协议,传输层软件TCP的目的是允许数据同网络上的另外站点进行可靠的交换。它能提供端口编号的译码,以识别主机的应用程序,而且完成数据的可靠传输。 • TCP 协议具有严格的内装差错检验算法确保数据的完整性。 • TCP 是面向字节的顺序协议,这意味着包内的每个字节被分配一个顺序编号,并分配给每包一个顺序编号。
用户数据报文协议 • UDP也是TCP/IP 的传输层协议,它是无连接的,不可靠的传输服务。当接收数据时它不向发送方提供确认信息,它不提供输入包的顺序,如果出现丢失包或重份包的情况,也不会向发送方发出差错报文。 • UDP 的主要作用是分配和管理端口编号,以正确无误的识别运行在网络站点上的个别应用程序。由于它执行功能时具有较低的开销,因而执行速度比TCP快。它多半用于不需要可靠传输的应用程序,例如网络视频点播和视频会议等。
Linux中TCP/IP分层结构(续) • 最上层是网络应用程序层,如WWW、FTP、E-mail等网络应用程序位于这一层。网络应用层通过BSD套接字进行数据传输。 • BSD套接字是最早的网络通信的实现,它由一个只处理BSD套接字的管理软件支持。 • 其下面是INET套接字层,它管理TCP协议和UDP协议的通信末端。UDP是无连接的协议,TCP则是一个可靠的端到端协议。 • IP层是用来实现网络间协议的,其中的代码主要是为上一层数据准备IP数据头,并且决定如何把接收到的IP数据包传送到上层。 • 在IP层下方是支持0整个Linux网络系统的网络设备。
网络编程基础 • socket概述 linux中的网络编程通过socket接口实现。Socket既是一种特殊的IO,它也是一种文件描述符。一个完整的Socket 都有一个相关描述{协议,本地地址,本地端口,远程地址,远程端口};每一个Socket 有一个本地的唯一Socket 号,由操作系统分配。
Socket类型 • 流式套接字(SOCK_STREAM): 流式的套接字可以提供可靠的、面向连接的通讯流。它使用了TCP协议。TCP 保证了数据传输的正确性和顺序性。 • 数据报套接字(SOCK_DGRAM): 数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错。使用数据报协议UDP协议。 • 原始套接字: 原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等。
套接字地址结构 struct sockaddr { unsigned short sa_family; /* address族, AF_xxx */ char sa_data[14]; /* 14 bytes的协议地址 */ }; sa_family 一般来说, IPV4使用“AF_INET”。 sa_data包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是杂溶在一起的
套接字地址结构(续) struct sockaddr_in { short int sin_family; /* Internet地址族 */ unsigned short int sin_port; /* 端口号 */ struct in_addr sin_addr; /* Internet地址 */ unsigned char sin_zero[8]; /* 添0(和struct sockaddr一样大小)*/ }; 这两个数据类型是等效的,可以相互转换,通常使用sockaddr_in更为方便
字节序列转换 因为每一个机器内部对变量的字节存储顺序不同(有的系统是高位在前,底位在后,而有的系统是底位在前,高位在后 ),而网络传输的数据大家是一定要统一顺序的。所以对与内部字节表示顺序和网络字节顺序不同的机器,就一定要对数据进行转换。
小实验 • 实验(一)(思考) 写个程序了解一下自己机器的字节顺序
union { short inum; char c[sizeof(short)]; }un; un.inum = 0x0102; if(un.c[0]==1 && un.c[1]==2) printf("big_endian.\n"); else if(un.c[0]==2 && un.c[1]==1) printf("little_endian.\n");
套接字字节转换 • htons()——“Host to Network Short” 主机字节顺序转换为网络字节顺序(对无符号短型进行操作2bytes) • htonl()——“Host to Network Long” 主机字节顺序转换为网络字节顺序(对无符号长型进行操作4bytes) • ntohs()——“Network to Host Short” 网络字节顺序转换为主机字节顺序(对无符号短型进行操作2bytes) • ntohl()——“Network to Host Long ” 网络字节顺序转换为主机字节顺序(对无符号长型进行操作4bytes)
地址格式转换 • Linux提供将点分格式的地址转于长整型数之间的转换函数。如: inet_addr()能够把一个用数字和点表示IP 地址的字符串转换成一个无符号长整型。 inet_ntoa()(“ntoa”代表“Network to ASCII”);包括:inet_aton, inet_ntoa, inet_addr等。
基本套接字函数(socket) • 为了建立Socket,程序可以调用Socket函数,该函数返回一个类似于文件描述符的句柄。socket函数原型为:int socket(int domain, int type, int protocol);domain指明所使用的协议族,通常为PF_INET,表示互联网协议族(TCP/IP协议族);type参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket(SOCK_RAW),允许程序使用低层协议;protocol通常赋值"0"。Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。
基本套接字函数(bind) • 通过socket调用返回一个socket描述符后,在使用socket进行网络传输以前,必须配置该socket。面向连接的socket客户端通过 调用Connect函数在socket数据结构中保存本地和远端信息。无连接socket的客户端和服务端以及面向连接socket的服务端通过调用bind函数来配置本地信息。Bind函数将socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。Bind函数原型为:int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
基本套接字函数(connect) • 面向连接的客户程序使用Connect函数来配置socket并与远端服务器建立一个TCP连接,其函数原型为:int connect(int sockfd, struct sockaddr *serv_addr,int addrlen);Sockfd 是socket函数返回的socket描述符;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是远端地质结构的长度。Connect函数在出现错误时返回-1,并且设置errno为相应的错误码。进行客户端程序设计无须调用bind(),因为这种情况下只需知道目的机器 的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候到 端口。
基本套接字函数(listen) • Listen函数使socket处于被动的监听模式,并为该socket建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。int listen(int sockfd,int backlog);Sockfd 是Socket系统调用返回的socket 描述符;backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()它们(参考下文)。Backlog对队列中等待 服务的请求的数目进行了限制,大多数系统缺省值为20。如果一个服务请求到来时,输入队列已满,该socket将拒绝连接请求,客户将收到一个出错信息。当出现错误时listen函数返回-1,并置相应的errno错误码。
基本套接字函数(accept) • accept()函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用accept函数,然后睡眠并等待客户的连接请求。int accept(int sockfd, void *addr, int *addrlen);sockfd是被监听的socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某 台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。出现错误时accept函数返回-1并置相应的errno值。
小实验 • 实验(二) 写一个服务器端程序创建一个服务器,再写一个客户端程序连接该服务器,连接上之后服务器端打印出客户端的IP地址
代码分析(服务器端) listenfd=socket(AF_INET,SOCK_STREAM,0); if(listenfd<0) { printf("Socket created failed\n"); return -1; } seraddr.sin_family=AF_INET; seraddr.sin_port=htons(6666); seraddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(listenfd,(struct sockaddr*)&seraddr,sizeof(seraddr))<0) { printf("bind failed\n"); return -1; } listen(listenfd,LISTENQ); len = sizeof(cliaddr); connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&len); printf("connect from %s,port %d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
代码分析(客户端) connfd=socket(AF_INET,SOCK_STREAM,0); if(socketfd<0) { printf("Socket created failed\n"); return -1; } seraddr.sin_family=AF_INET; seraddr.sin_port=htons(6666); seraddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(connect(connfd,(struct sockaddr*)&seraddr,sizeof(seraddr))<0) { printf("Connect server failed.\n"); return -1; }
基本套接字函数(read/write) • 函数read和write从套接字读和写数据。其定义如下: int read(int fd,char *buf,int len); / int write(int fd,char *buf, int len); 参数fd指定读写操作的套接字描述符;函数read的参数buf指定接收数据缓冲区,函数write的参数buf指定发送数据缓冲区;参数len指定接收或发送的数据量大小。函数read成功执行时,返回独到的数据量大小,否则返回-1。函数write成功执行时,返回写入的数据量大小,否则返回-1。
小实验 实验(三) 编写一个客户机/服务器程序,其中客户机使用流套接字向服务器请求日期和时间,服务器在收到之后,回答请求并显示客户机的地址
服务器端 ticks=time(NULL); sprintf(buff,”%.24s\r\n”,ctime(&ticks)); write(connfd,buff,strlen(buff)); close(connfd);
客户端 while((n=read(sockfd,recvbuff,256))>0) { recvbuff[n]=0; fputs(recvbuff,stdout); }
如何解决程序阻塞问题 • 程序只有一个进程,在read()——等待网络数据的时候,会进入阻塞状态,点解? • read函数只是一个通用的读文件设备的接口。是否阻塞需要由设备的属性和设定所决定。一般来说,读字符终端、网络的socket描述字,管道文件等,这些文件的缺省read都是阻塞的方式。如果是读磁盘上的文件,一般不会是阻塞方式的。 • 解决方法:多加一个?
界面如何设计 • 主机:能显示主机IP;有确认、取消连接的功能; • 客机:输入、显示想要连接的IP(软键盘);
界面设计 • 为了在QT中使建立网络主机和连接网络主机能够界面化,接下来介绍几个类: QLabel QLineEdit QGridLayout QDialog
QLabel • QLabel是继承了QWidget,它提供了显示文本或者图像的功能。其最常用的成员函数是virtual void setText( const QString & ),这是个虚拟可重写的函数,其参数是一个QString类的对象,QString类提供了一个Unicode文本和经典的C以零结尾的字符数组的抽象,它可以与其他类型数据如int / char 相互转换。
小实验 • 实验(一) 在自定义窗口中,添加按钮,在按下按钮之后,在其下方出现一行标签