《2022年Linux+C网络编程 .pdf》由会员分享,可在线阅读,更多相关《2022年Linux+C网络编程 .pdf(9页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Linux C编程-网络编程摘要网络编程,一定离不开套接口;那什么是套接口呢?在Linux 下,所有的I/O 操作都是通过读写文件描述符而产生的,文件描述符是一个和打开的文件相关联的整数,这个文件并不只包括真正存储在磁盘上的文件,还包括一个网络连接、一个命名管道、一个终端等,而套接口就是系统进程和文件描述符通信的一种方法。目前最常用的套接口是字:字节流套接口(基于 TCP)和数据报套接口(基于 UDP),当然还有原始套接口(原始套接口提供TCP 套接口和UDP 套接口所不提供的功能,如构造自己的TCP 或 UDP 分组)等,我们这里主要介绍字节流套接口和数据报套接口。By Wing网络编程,一
2、定离不开套接口;那什么是套接口呢?在Linux 下,所有的I/O 操作都是通过读写文件描述符而产生的,文件描述符是一个和打开的文件相关联的整数,这个文件并不只包括真正存储在磁盘上的文件,还包括一个网络连接、一个命名管道、一个终端等,而套接口就是系统进程和文件描述符通信的一种方法。目前最常用的套接口是字:字节流套接口(基于 TCP)和数据报套接口(基于 UDP),当然还有原始套接口(原始套接口提供TCP 套接口和UDP 套接口所不提供的功能,如构造自己的TCP 或 UDP 分组)等,我们这里主要介绍字节流套接口和数据报套接口。要学习网络编程,一定离不开网络库的函数,在 Linux 系统下,可以用
3、 man 函数名 来得到这个函数的帮助,不过为了照顾E 文不大好的朋友,下面就将常用的网络函数和用法列出来供大家参考:、socket函数:为了执行网络输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符。-#include int socket(int family,int type,int protocol);返回:非负描述字成功-1失败-名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 9 页 -第一个参数指明了协议簇,目前支持 5种协议簇,最常用的有AF_INET(IPv4 协议)和 AF_INET6(IPv6协议);第二个参数指明套接口类型,有三种类
4、型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)和 SOCK_RAW(原始套接口);如果套接口类型不是原始套接口,那么第三个参数就为0。、connect函数:当用socket建立了套接口后,可以调用connect为这个套接字指明远程端的地址;如果是字节流套接口,connect就使用三次握手建立一个连接;如果是数据报套接口,connect仅指明远程端地址,而不向它发送任何数据。-#include int connect(int sockfd,const struct sockaddr*servaddr,socklen_t addrlen);返回:0成功-1失败
5、-第一个参数是socket 函数返回的套接口描述字;第二和第三个参数分别是一个指向套接口地址结构的指针和该结构的大小。这些地址结构的名字均已“sockaddr_”开头,并以对应每个协议族的唯一后缀结束。以IPv4套接口地址结构为例,它以“sockaddr_in”命名,定义在头文件;以下是结构体的内容:-struct in_addr in_addr_t s_addr;/*IPv4 地址*/;struct sockaddr_in uint8_t sin_len;/*无符号的 8位整数*/sa_family_t sin_family;/*套接口地址结构的地址簇,这里为AF_INET*/in_port
6、_t sin_port;/*TCP或 UDP 端口*/struct in_addr sin_addr;char sin_zero8;-、bind 函数:为套接口分配一个本地IP 和协议端口,对于网际协议,协议地址是32位 IPv4 地址或 128位 IPv6 地址与 16位的 TCP 或 UDP 端口号的组合;如指定端口为0,调用 bind 时内核将选择一个临时端口,名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 9 页 -如果指定一个通配IP 地址,则要等到建立连接后内核才选择一个本地IP 地址。-#include int bind(int sockfd,const struc
7、t sockaddr*myaddr,socklen_t addrlen);返回:0成功-1失败-第一个参数是socket 函数返回的套接口描述字;第二和第第三个参数分别是一个指向特定于协议的地址结构的指针和该地址结构的长度。、listen函数:listen函数仅被TCP 服务器调用,它的作用是将用sock创建的主动套接口转换成被动套接口,并等待来自客户端的连接请求。-#include int listen(int sockfd,int backlog);返回:0成功-1失败-第一个参数是socket 函数返回的套接口描述字;第二个参数规定了内核为此套接口排队的最大连接个数。由于 listen
8、函数第二个参数的原因,内核要维护两个队列:以完成连接队列和未完成连接队列。未完成队列中存放的是TCP 连接的三路握手为完成的连接,accept 函数是从以连接队列中取连接返回给进程;当以连接队列为空时,进程将进入睡眠状态。、accept函数:accept函数由 TCP 服务器调用,从已完成连接队列头返回一个已完成连接,如果完成连接队列为空,则进程进入睡眠状态。-#include int accept(int sockfd,struct sockaddr*cliaddr,socklen_t*addrlen);回:非负描述字成功-1失败-名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共
9、 9 页 -第一个参数是socket 函数返回的套接口描述字;第二个和第三个参数分别是一个指向连接方的套接口地址结构和该地址结构的长度;该函数返回的是一个全新的套接口描述字;如果对客户段的信息不感兴趣,可以将第二和第三个参数置为空。、inet_pton函数:将点分十进制串转换成网络字节序二进制值,此函数对IPv4 地址和 IPv6 地址都能处理。-#include int inet_pton(int family,const char*strptr,void*addrptr);返回:1成功0输入不是有效的表达格式-1失败-第一个参数可以是AF_INET或 AF_INET6:第二个参数是一个指向
10、点分十进制串的指针:第三个参数是一个指向转换后的网络字节序的二进制值的指针。、inet_ntop函数:和inet_pton函数正好相反,inet_ntop函数是将网络字节序二进制值转换成点分十进制串。-#include const char*inet_ntop(int family,const void*addrptr,char*strptr,size_t len);返回:指向结果的指针成功NULL 失败-第一个参数可以是AF_INET或 AF_INET6:第二个参数是一个指向网络字节序的二进制值的指针;第三个参数是一个指向转换后的点分十进制串的指针;第四个参数是目标的大小,以免函数溢出其调用
11、者的缓冲区。、fock 函数:在网络服务器中,一个服务端口可以允许一定数量的客户端同时连接,这时单进程是不可能实现的,而fock 就分配一个子进程和客户端会话,当然,这只是fock 的一个典型应用。名师资料总结-精品资料欢迎下载-名师精心整理-第 4 页,共 9 页 -#include pid_t fock(void);返回:在子进程中为0,在父进程中为子进程ID-1失败-fock 函数调用后返回两次,父进程返回子进程ID,子进程返回 0。有了上面的基础知识,我们就可以进一步了解TCP 套接口和UDP 套接口、TCP 套接口TCP 套接口使用TCP 建立连接,建立一个TCP 连接需要三次握手,
12、基本过程是服务器先建立一个套接口并等待客户端的连接请求;当客户端调用connect 进行主动连接请求时,客户端TCP 发送一个SYN,告诉服务器客户端将在连接中发送的数据的初始序列号;当服务器收到这个SYN 后也给客户端发一个SYN,里面包含了服务器将在同一连接中发送的数据的初始序列号;最后客户在确认服务器发的SYN。到此为止,一个TCP 连接被建立。下面就用一个例子来说明服务器和客户是怎么连接的-/*client.c*/#include#include#include#include#include#include#include#include int main(int argc,char
13、*argv)int sockfd,numbytes;char buf100;struct hostent*he;struct sockaddr_in their_addr;名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 9 页 -int i=0;/将基本名字和地址转换he=gethostbyname(argv1);/建立一个TCP 套接口if(sockfd=socket(AF_INET,SOCK_STREAM,0)=-1)perror(socket);exit(1);/初始化结构体,连接到服务器的2323 端口their_addr.sin_family=AF_INET;their
14、_addr.sin_port=htons(2323);their_addr.sin_addr=*(struct in_addr*)he-h_addr);bzero(&(their_addr.sin_zero),8);/和服务器建立连接if(connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr)=-1)perror(connect);exit(1);/向服务器发送字符串hello!if(send(sockfd,hello!,6,0)=-1)perror(send);exit(1);/接受从服务器返回的信息if(nu
15、mbytes=recv(sockfd,buf,100,0)=-1)perror(recv);exit(1);bufnumbytes=;printf(result:%s,buf);close(sockfd);return 0;-/*server.c*/#include#include#include#include#include#include#include#include main()名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 9 页 -int sockfd,new_fd;struct sockaddr_in my_addr;struct sockaddr_in thei
16、r_addr;int sin_size;/建立 TCP 套接口if(sockfd=socket(AF_INET,SOCK_STREAM,0)=-1)perror(socket);exit(1);/初始化结构体,并绑定2323 端口my_addr.sin_family=AF_INET;my_addr.sin_port=htons(2323);my_addr.sin_addr.s_addr=INADDR_ANY;bzero(&(my_addr.sin_zero),8);/绑定套接口if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct soc
17、kaddr)=-1)perror(bind);exit(1);/创建监听套接口if(listen(sockfd,10)=-1)perror(listen);exit(1);/等待连接while(1)sin_size=sizeof(struct sockaddr_in);perror(server is run);/如果建立连接,将产生一个全新的套接字if(new_fd=accept(sockfd,(struct sockaddr*)&their_addr,&sin_size)=-1)perror(accept);exit(1);/生成一个子进程来完成和客户端的会话,父进程继续监听if(!for
18、k()/读取客户端发来的信息if(numbytes=recv(new_fd,buff,strlen(buff),0)=-1)perror(recv);exit(1);printf(%s,buff);名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 9 页 -/将从客户端接收到的信息再发回客户端if(send(new_fd,buff,strlen(buff),0)=-1)perror(send);close(new_fd);exit(0);close(new_fd);close(sockfd);-现在让我们来编译这两个程序:rootlinuxaid#gcc-o server serv
19、er.c rootlinuxaid#gcc-o client client.c 然后在一台计算机上先运行服务器程序,再在另一个终端上运行客户端就会看到结果;如果不运行服务器程序而先运行客户程序将立即提示Connect:Connection refused,这就是 TCP 套接口的好处,如果是 UDP套接口将会有一个延时才会得到错误信息(UDP 套接口后面有介绍)。建立一个 TCP 连接需要三次握手,而断开一个TCP 则需要四个分节。当某个应用进程调用close(主动端)后(可以是服务器端,也可以是客户端),这一端的TCP 发送一个FIN,表示数据发送完毕;另一端(被动端)发送一个确认,当被动端
20、待处理的应用进程都处理完毕后,发送一个 FIN 到主动端,并关闭套接口,主动端接收到这个 FIN 后再发送一个确认,到此为止这个TCP 连接被断开。、UDP 套接口UDP 套接口是无连接的、不可靠的数据报协议;既然他不可靠为什么还要用呢?其一:当应用程序使用广播或多播是只能使用UDP 协议;其二:由于他是无连接的,所以速度快。因为UDP 套接口是无连接的,如果一方的数据报丢失,那另一方将无限等待,解决办法是设置一个超时。在编写 UDP 套接口程序时,有几点要注意:建立套接口时socket 函数的第二个参数应该是SOCK_DGRAM,说明是建立一个UDP 套接口;由于 UDP 是无连接的,所以服
21、务器端并不需要listen 或 accept函数;当 UDP 套接口调用connect 函数时,内核只记录连接放的IP 地址和端口,并立即返回给调用进程,正名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 9 页 -因为这个特性,UDP 服务器程序中并不使用fock 函数,用单进程就能完成所有客户的请求。网络编程中设计并发服务器,使用多进程与 多线程,请问有什么区别?答案一:1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
22、两者都可以提高程序的并发度,提高程序运行效率和响应时间。线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在 SMP机器上运行,而进程则可以跨机器迁移。答案二:根本区别就一点:用多进程每个进程有自己的地址空间(address space),线程则共享地址空间。所有其它区别都是由此而来的:1。速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。2。资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。3。同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。名师资料总结-精品资料欢迎下载-名师精心整理-第 9 页,共 9 页 -