您好,欢迎访问三七文档
第7章TCP网络编程基础TCP协议是TCP/IP协议中很重要的一个协议,它由于传输的稳定性,在很多程序中都在使用,例如HTTP、FTP等协议都是在TCP的基础上进行构建的。本章介绍TCP套接字的编程基础知识,主要包含如下内容:套接字编程的基础知识的部分,介绍套接字编程中经常使用的套接字地址结构,对内核和应用层之间的内存数据传递方式进行了简单的介绍。TCP网络编程的流程部分,简单介绍TCP套接字服务器、客户端的编程框架,对函数socket()、bind()、listen()、accept()、connect()、close()函数进行了介绍,并提及如何使用read()和write()函数进行数据的读取和发送。7.1套接字编程基础知识在进行套接字编程之前需要对基本的数据结构有所了解。本节对套接字的地址结构定义的形式、如何使用套接字的地址结构进行详细的介绍,并且对Linux操作系统中用户空间和用户空间之间的交互过程进行简单的介绍,用户对网络程序设计的方法有比较深入的了解。7.1.1套接字地址结构进行套接字编程需要指定套接字的地址作为参数,不同的协议族有不同的地址结构定义方式。这些地址结构通常以sockaddr_开头,每一个协议族有一个唯一的后缀,例如对于以太网,其结构名称为sockaddr_in。1.通用套接字数据结构2.实际使用的套接字数据结构3.结构sockaddr和结构sockaddr_in的关系7.1.1套接字地址结构sin_lenu8sin_family;u16sin_port;structin_addrsin_addrcharsin_zero[8];sa_family_tsa_family;charsa_data[14];01247150215structsockaddr_instructsockaddr7.1.2用户层和内核层交互过程套接字参数中有部分参数是需要用户传入的,这些参数用来与Linux内核进行通信,例如指向地址结构的指针。通常是采用内存复制的方法进行。例如bind()函数需要传入地址结构structsockaddr*my_addr和my_addr指向参数的长度。1.向内核传入数据的交互过程2.内核传出数据的交互过程7.1.2用户层和内核层交互过程用户空间内核空间长度(int)套接字地址结构网络协议栈传入传入7.2TCP网络编程流程TCP网络编程是目前比较通用的方式,例如HTTP协议、FTP协议等很多广泛应用的协议均基于TCP协议。TCP编程主要为C/S模式,即服务器(S)、客户端(C)模式。TCP网络编程的流程包含服务器和客户端两种模式,这两种模式之间的程序设计的流程存在很大的差别。7.2.1TCP网络编程架构TCP网络编程有两种模式,一种是服务器模式,另一种是客户端模式。服务器模式创建一个服务程序,等待客户端用户的连接,接收到用户的连接请求后,根据用户的请求进行处理;客户端模式则根据目的服务器的地址和端口进行连接,向服务器发送请求并对服务器的响应进行数据处理。1.服务器端的程序设计模式2.客户端的程序设计模式3.客户端与服务器的交互过程7.2.1TCP网络编程架构开始socket()bind()listen()结束服务器端模式开始socket()connect()结束客户端模式close()close()accept()read()write()write()read()三次握手连接写入数据读取数据关闭过程7.2.2创建网络插口函数socket()网络程序设计中的套接字系统调用函数socket()用来获得文件描述符。1.函数socket()介绍2.应用层函数socket()和内核函数之间的关系名称含义PF_UNIX,PF_LOCAL本地通信PF_INETIPv4Internet协议PF_INET6IPv6Internet协议PF_IPXIPX-Novell协议PF_NETLINK内核用户界面设备PF_X25ITU-TX.25/ISO-8208协议PF_AX25AmateurradioAX.25协议PF_ATMPVC原始ATMPVC访问PF_APPLETALKAppletalkPF_PACKET底层包访问7.2.2创建网络插口函数socket()Intsock=socket(AF_INET,SOCK_STREAM,0);应用层内核层intsock=sys_socket(AF_INET,SOCK_STREAM,0);structsocket*sock;retval=sock_create(AF_INET,SOCK_STREAM,0,&sock);intretval;retval=sock_map_fd(sock);7.2.3绑定一个地址端口对bind()在建立套接字文件描述符成功后,需要对套接字进行地址和端口的绑定,才能进行数据的接收和发送操作。1.函数bind()介绍2.函数bind()的例子3.应用层bind()函数和内核函数之间的关系值含义备注EADDRINUSE给定地址已经使用EBADFsockfd不合法EINVALsockfd已经绑定到其他地址ENOTSOCKsockfd是一个文件描述符,不是socket描述符EACCES地址被保护,用户的权限不足EADDRNOTAVAIL接口不存在或者绑定地址不是本地UNIX协议族,AF_UNIXEFAULTmy_addr指针超出用户空间UNIX协议族,AF_UNIXEINVAL地址长度错误,或者socket不是AF_UNIX族UNIX协议族,AF_UNIXELOOP解析my_addr是符号链接过多UNIX协议族,AF_UNIXENAMETOOLONGmy_addr过长UNIX协议族,AF_UNIXENOENT文件不存在UNIX协议族,AF_UNIXENOMEM内存内核不足UNIX协议族,AF_UNIXENOTDIR不是目录UNIX协议族,AF_UNIXEROFSsocket节点应该在只读文件系统上UNIX协议族,AF_UNIX7.2.3绑定一个地址端口对bind()bind(sockfd,(structsockaddr*)&my_addr,sizeof(structsockaddr));应用层内核层longsys_bind(sockfd,(structsockaddr*)&my_addr,sizeof(structsockaddr))structsocket*sock;interr,fput_needed;sock=sockfd_lookup_light(sockfd,&err,&fput_needed);charaddress[MAX_SOCK_ADDR];move_addr_to_kernel(umyaddr,addrlen,address);err=sock-ops-bind(sock,(structsockaddr*)address,addrlen);AF_INET族……intinet_bind(structsocket*sock,structsockaddr*uaddr,intaddr_len)……bind函数对应的系统调用函数sys_bind函数sockfd_lookup_light获得文件描述符sockfd对应的内核结构structsock的指针函数move_addr_to_kernel将应用层的地址移到内核层,放到address中调用应用层函数bind对应协议族的bind函数实现协议族AF_INET的bind函数实现为inet_bindbind函数将某个端口和IP地址绑到sockfd上7.2.4监听本地端口listen在7.2.1小节中简单介绍了服务器模式的方式,服务器模式中有listen()和accept()两个函数,而客户端则不需要这两个函数。函数listen()用来初始化服务器可连接队列,服务器处理客户端连接请求的时候是顺序处理的,同一时间仅能处理一个客户端连接。当多个客户端的连接请求同时到来的时候,服务器并不是同时处理,而是讲不能进行处理的客户端连接请求放到等待队列中,这个队列的长度由listen()函数来定义。1.函数listen()介绍2.函数listen()的例子3.应用层listen()函数和内核函数之间的关系7.2.4监听本地端口listen值含义EADDRINUSE另一个socket已经在同一端口侦听EBADF参数sockfd不是合法的描述符ENOTSOCK参数sockfd不是代表socket的文件描述符EOPNOTSUPPsocket不支持listen操作7.2.4监听本地端口listenlisten(sockfd,5);应用层内核层longsys_listen(sockfd,5)structsocket*sock;interr,fput_needed;sock=sockfd_lookup_light(sockfd,&err,&fput_needed);somaxconn=sock_net(sock-sk)-core.sysctl_somaxconn;if((unsigned)backlogsomaxconn)backlog=somaxconn;err=sock-ops-listen(sock,5);AF_INET族……intinet_listen(structsocket*sock,intbacklog)……listen函数对应的系统调用函数sys_listen函数sockfd_lookup_light获得文件描述符sockfd对应的内核结构structsock的指针调整用户最大侦听队列数的不合法设置调用应用层函数liten对应协议族的listen函数实现协议族AF_INET的listen函数实现为inet_listenlisten函数将sockfd对应的socket的侦听等待队列设置为57.2.5接受一个网络请求accept()当一个客户端的连接请求到达服务器主机侦听的端口时,此时客户端的连接会在队列中等待,直到使用服务器处理接收请求。函数accept()成功执行后,会返回一个新的套接口文件描述符来表示客户端的连接,客户端连接的信息可以通过这个新描述符来获得。因此当服务器成功处理客户端的请求连接后,会有两个文件描述符,老的文件描述符表示正在监听的socket,新产生的文件描述符表示客户端的连接,函数send()和recv()通过新的文件描述符进行数据收发。1.函数accept()介绍2.函数accept()的例子3.应用层accept()函数和内核函数之间的关系7.2.5接受一个网络请求accept()client_fd=accept(sockfd,&client_addr,&addr_length);应用层内核层longsys_accept(intfd,structsockaddr__user*upeer_sockaddr,int__user*upeer_addrlen)structsocket*sock;interr,fput_needed;sock=sockfd_lookup_light(sockfd,&err,&fput_needed);structsocket*newsock=sock_alloc();newsock-type=sock-type;newsock-ops=sock-ops;newfd=sock_alloc_fd(&newfile);err=sock-ops-accept(sock,newsock,sock-file-f_flags);AF_INET族……intinet_accept(structsocket*sock,structsocket*newsock,intflags)……intinet_getname(structsocket*sock,structsockaddr*uaddr,int*uaddr_len,intpeer)……accept函数对应的系统调用函数sys_accept函数sockfd_lookup_light获得文件描述符so
本文标题:TCP网络编程基础
链接地址:https://www.777doc.com/doc-4606914 .html