Linux环境下的网络编程

Linux 专栏收录该内容
11 篇文章 0 订阅

xuyb 
                Email:bai_xy@21cn.com 
     本文介绍了在Linux环境下的socket编程常用函数用法及socket编程的一般规则和客户/服务器模型的编程应注意的事项和常遇问题的解决方法,并举了具体代  码实例。要理解本文所谈的技术问题需要读者具有一定C语言的编程经验和TCP/IP方面的基本知识。要实习本文的示例,需要Linux下的gcc编译平台支持。  
     Socket定义  
     网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用—Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。常用 的Socket类型有两种:流式Socket—SOCK_STREAM和数据报式Socket—SOCK_DGRAM。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。  
    Socket编程相关数据类型定义  
    计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。Intenet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换。  
   我们要讨论的第一个结构类型是:struct sockaddr,该类型是用来保存socket信息的:  
     struct sockaddr {  
      unsigned short sa_family; /* 地址族, AF_xxx */  
      char sa_data[14]; /* 14 字节的协议地址 */ };  
     sa_family一般为AF_INET;sa_data则包含该socket的IP地址和端口号。  
     另外还有一种结构类型:  
     struct sockaddr_in {  
      short int sin_family; /* 地址族 */  
      unsigned short int sin_port; /* 端口号 */  
      struct in_addr sin_addr; /* IP地址 */  
      unsigned char sin_zero[8]; /* 填充0 以保持与struct sockaddr同样大 
   小 */  
     };  
     这个结构使用更为方便。sin_zero(它用来将sockaddr_in结构填充到与struct sockaddr同样的长度)应该用bzero()或memset()函数将其置为零。指向sockaddr_in 的指针和指向sockaddr的指针可以相互转换,这意味着如果一个函数所需参数类型是sockaddr时,你可以在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或者相反。sin_family通常被赋AF_INET;in_port和sin_addr应该转换成为网络字节优先顺序;而sin_addr则不需要转换。  
 我们下面讨论几个字节顺序转换函数:  
  htons()--"Host to Network Short" ; htonl()--"Host to Network long" 
  ntohs()--"Network to Host Short" ; ntohl()--"Network to Host Long" 
  在这里, h表示"host" ,n表示"network",s 表示"short",l表示 "long" 
   。  
   打开socket 描述符、建立绑定并建立连接  
   socket函数原型为:  
  int socket(int domain, int type, int protocol);  
  domain参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM;protocol通常赋值“0”。Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。一旦通过socket调用返回一个socket描述符,你应该将该socket与你本机上的一个端口相关联(往往当你在设计服务器端程序时需要调用该函数。随后你就可以在该端口监听服务请求;而客户端一般无须调用该函数)。 Bind函数原型为 :  
  int bind(int sockfd,struct sockaddr *my_addr, int addrlen);  
  Sockfd是一个socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被设置为sizeof(struct sockaddr)。  
 最后,对于bind 函数要说明的一点是,你可以用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号:  
     my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */  
     my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */  
  通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。同样,通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。Bind()函数在成功被调用时返回0;遇到错误时返回“-1”并将errno置为相应的错误号。另外要注意的是,当调用函数时,一般不要将端口号置为小于1024的值,因为1~1024是保留端口号,你可以使用大于1024中任何一个没有被占用的端口号。  
  Connect()函数用来与远端服务器建立一个TCP连接,其函数原型为:  
  int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);  
 Sockfd是目的服务器的sockt描述符;serv_addr是包含目的机IP地址和端口号的指针。遇到错误时返回-1,并且errno中包含相应的错误码。进行客户端程序设计无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,内核会自动选择一个未被占用的端口供客户端来使用。  
  Listen()——监听是否有服务请求  
  在服务器端程序中,当socket与某一端口捆绑以后,就需要监听该端口,以便对到达的服务请求加以处理。  
  int listen(int sockfd, int backlog);  
 Sockfd是Socket系统调用返回的socket 描述符;backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()它们(参考下文)。cklog对队列中等待服务的请求的数目进行了限制,大多数系统缺省值为20。 
  当listen遇到错误时返回-1,errno被置为相应的错误码。  
 故服务器端程序通常按下列顺序进行函数调用:  
 socket(); bind(); listen(); /* accept() goes here */  
  accept()——连接端口的服务请求。  
  当某个客户端试图与服务器监听的端口连接时,该连接请求将排队等待服务器accept()它。通过调用accept()函数为其建立一个连接,accept()函数将返回一个新的socket描述符,来供这个新连接来使用。而服务器可以继续在以前的那个socket上监听,同时可以在新的socket描述符上进行数据send()(发送)和recv()(接收)操作:  
  int accept(int sockfd, void *addr, int *addrlen);  
  sockfd是被监听的socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。错误发生时返回一个-1并且设置相应的errno值。  
  Send()和recv()——数据传输  
  这两个函数是用于面向连接的socket上进行数据传输。  
  Send()函数原型为:  
  int send(int sockfd, const void *msg, int len, int flags);  
  Sockfd是你想用来传输数据的socket描述符,msg是一个指向要发送数据的指针。  
  Len是以字节为单位的数据的长度。flags一般情况下置为0(关于该参数的用法可参照man手册)。  
  char *msg = "Beej was here!"; int len, bytes_sent; ... ...  
  len = strlen(msg); bytes_sent = send(sockfd, msg,len,0); ... ...  
  Send()函数返回实际上发送出的字节数,可能会少于你希望发送的数据。所以需要对send()的返回值进行测量。当send()返回值与len不匹配时,应该对这种情况进行处理。  
  recv()函数原型为:  
  int recv(int sockfd,void *buf,int len,unsigned int flags);  
  Sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。Flags也被置为0。Recv()返回实际上接收的字节数,或当出现错误时,返回-1并置相应的errno值。  
  Sendto()和recvfrom()——利用数据报方式进行数据传输  
  在无连接的数据报socket方式下,由于本地socket并没有与远端机器建立连接,所以在发送数据时应指明目的地址,sendto()函数原型为:  
  int sendto(int sockfd, const void *msg,int len,unsigned int flags, const struct sockaddr *to, int tolen);  
  该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。  
  Recvfrom()函数原型为:  
  int recvfrom(int sockfd,void *buf,int len,unsigned int lags,struct sockaddr *from,int *fromlen);  
  from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。  
  应注意的一点是,当你对于数据报socket调用了connect()函数时,你也可以利用send()和recv()进行数据传输,但该socket仍然是数据报socket,并且利用传输层的UDP服务。但在发送或接收数据报时,内核会自动为之加上目地和源地址信息。  
  Close()和shutdown()——结束数据传输  
  当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而 
   停止在该socket上的任何数据操作:close(sockfd);  
   你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。  
  int shutdown(int sockfd,int how);  
  Sockfd的含义是显而易见的,而参数 how可以设为下列值:  
  ·0-------不允许继续接收数据  
  ·1-------不允许继续发送数据  
  ·2-------不允许继续发送和接收数据,均为允许则调用close ()  
  shutdown在操作成功时返回0,在出现错误时返回-1(并置相应errno)。  

  DNS——域名服务相关函数  
  由于IP地址难以记忆和读写,所以为了读写记忆方便,人们常常用域名来表示主机,这就需要进行域名和IP地址的转换。函数gethostbyname()就是完成这种转换的,函数原型为:  
     struct hostent *gethostbyname(const char *name);  
     函数返回一种名为hosten的结构类型,它的定义如下:  
     struct hostent {  
      char *h_name; /* 主机的官方域名 */  
      char **h_aliases; /* 一个以NULL结尾的主机别名数组 */  
      int h_addrtype; /* 返回的地址类型,在Internet环境下为AF-INET */  

      int h_length; /*地址的字节长度 */  
      char **h_addr_list; /* 一个以0结尾的数组,包含该主机的所有地址*/  

     };  
     #define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/  
     当 gethostname()调用成功时,返回指向struct hosten的指针,当调用失败时返回-1。当调用gethostbyname时,你不能使用perror()函数来输出错误信息,而应该使用herror()函数来输出。  
   面向连接的客户/服务器代码实例  
  这个服务器通过一个连接向客户发送字符串"Hello,world!"。只要在服务器上运行该服务器软件,在客户端运行客户软件,客户端就会收到该字符串。 
  该服务器软件代码见程序1:  
     #include stdio.h  
     #include stdlib.h  
     #include errno.h  
     #include string.h  
     #include sys/types.h  
     #include netinet/in.h  
     #include sys/socket.h  
     #include sys/wait.h  
     #define MYPORT 3490 /*服务器监听端口号 */  
     #define BACKLOG 10 /* 最大同时连接请求数 */  
     main()  
     {  
     intsock fd,new_fd; /* 监听socket: sock_fd,数据传输socket:new_fd*/  
     struct sockaddr_in my_addr; /* 本机地址信息 */  
     struct sockaddr_in their_addr; /* 客户地址信息 */  
     int sin_size;  
     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { /*错误检测  */  
     perror("socket"); exit(1); }  
     my_addr.sin_family=AF_INET;  
     my_addr.sin_port=htons(MYPORT);  
     my_addr.sin_addr.s_addr = INADDR_ANY;  
     bzero(&(my_addr.sin_zero),8);  
     if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))   
      == -1) {/*错误检测*/  
     perror("bind"); exit(1); }  
     if (listen(sockfd, BACKLOG) == -1) {/*错误检测*/  
     perror("listen"); exit(1); }  
     while(1) { /* main accept() loop */  
     sin_size = sizeof(struct sockaddr_in);  
     if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr,   
     &sin_size)) == -1) {  
     perror("accept"); continue; }  
     printf("server: got connection from %s",inet_ntoa(their_addr.sin_addr));
     if (!fork()) { /* 子进程代码段 */  
     if (send(new_fd, "Hello, world!", 14, 0) == -1)  
     perror("send"); close(new_fd); exit(0); }  
     close(new_fd); /* 父进程不再需要该socket */  
     waitpid(-1,NULL,WNOHANG) > 0 /*等待子进程结束,清除子进程所占用资源*/  
     }  
     }  
     (程序1)  
     服务器首先创建一个Socket,然后将该Socket与本地地址/端口号捆绑,成功之后就在相应的socket上监听,当accpet捕捉到一个连接服务请求时,就生成一个新的socket,并通过这个新的socket向客户端发送字符串"Hello,world!",然后关闭该socket。  
  fork()函数生成一个子进程来处理数据传输部分,fork()语句对于子进程返回的值为0。所以包含fork函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。  
  客户端软件代码部分见程序2:  
     #includestdio.h  
     #include stdlib.h  
     #include errno.h  
     #include string.h  
     #include netdb.h  
     #include sys/types.h  
     #include netinet/in.h  
     #include sys/socket.h  
     #define PORT 3490  
     #define MAXDATASIZE 100 /*每次最大数据传输量 */  
     int main(int argc, char *argv[])  
     {  
     int sockfd, numbytes;  
     char buf[MAXDATASIZE];  
     struct hostent *he;  
     struct sockaddr_in their_addr;  
     if (argc != 2) {  
     fprintf(stderr,"usage: client hostname"); exit(1); }  
     if((he=gethostbyname(argv[1]))==NULL) {  
     herror("gethostbyname"); exit(1); }  
     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {  
     perror("socket"); exit(1); }  
     their_addr.sin_family=AF_INET;  
     their_addr.sin_port=htons(PORT);  
     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); }  
     if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {  
     perror("recv"); exit(1); }  
     buf[numbytes] = '';  
     printf("Received: %s",buf);  
     close(sockfd);  
     return 0;  
     }  
     (程序2)  
  客户端代码相对来说要简单一些,首先通过服务器域名获得其IP地址,然后创建一个socket,调用connect函数与服务器建立连接,连接成功之后接收从服务器发送过来的数据,最后关闭socket,结束程序。  
 无连接的客户/服务器程序的在原理上和连接的客户/服务器是一样的,两者的区别在于无连接的客户/服务器中的客户一般不需要建立连接,而且在发送接收数据时,需要指定远端机的地址。  
  关于阻塞(blocking)的概念和select()函数当服务器运行到accept语句时,而没有客户连接服务请求到来,那么会发生什么情况?这时服务器就会停止在accept语句上等待连接服务请求的到来;同样,当程序运行到接收数据语句时,如果没有数据可以读取,则程序同样会停止在接收语句上。这种情况称为blocking。但如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接;否则就继续做其他事情,则可以通过将Socke设置为非阻塞方式来实现:非阻塞socket在没有客户在等待时就使accept调用立即返回。  
     #include unistd.h  
     #include fcntl.h  
     . . . . ; sockfd = socket(AF_INET,SOCK_STREAM,0);  
     fcntl(sockfd,F_SETFL,O_NONBLOCK); . . . . .  
  通过设置socket为非阻塞方式,可以实现“轮询”若干Socket。当企图从一个没有数据等待处理的非阻塞Socket读入数据时,函数将立即返回,并且返回值置为-1,并且errno置为EWOULDBLOCK。但是这种“轮询”会使CPU处于忙等待方式,从而降低性能。考虑到这种情况,假设你希望服务器监听连接服务请求的同时从已经建立的连接读取数据,你也许会想到用一个accept语句和多个recv()语句,但是由于accept及recv都是会阻塞的,所以这个想法显然不会成功。  
  调用非阻塞的socket会大大地浪费系统资源。而调用select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费CPU开销。Select函数原型为:  
 int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exeptfds,struct timeval *timeout);  
  其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以从标准输入和某个socket描述符读取数据,你只需要将标准输入的文件描述符0和相应的sockdtfd加入到readfds集合中;numfds的值是需要检查的号码最高的文件描述符加1,这个例子中numfds的值应为sockfd+1;当select返回时,readfds将被修改,指示某个文件描述符已经准备被读取,你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试,它提供了一组宏:  
     FD_ZERO(fd_set *set)----清除一个文件描述符集;  
     FD_SET(int fd,fd_set *set)----将一个文件描述符加入文件描述符集中; 
     
     FD_CLR(int fd,fd_set *set)----将一个文件描述符从文件描述符集中清除 
   ;  
     FD_ISSET(int fd,fd_set *set)----试判断是否文件描述符被置位。  
     Timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。struct timeval数据结构为: 
     
     struct timeval {  
      int tv_sec; /* seconds */  
      int tv_usec; /* microseconds */  
     };  
     我们通过程序3来说明:  
     #include sys/time.h  
     #include sys/types.h  
     #include unistd.h  
     #define STDIN 0 /*标准输入文件描述符*/  
     main()  
     {  
      struct timeval tv;  
      fd_set readfds;  
      tv.tv_sec = 2;  
      tv.tv_usec = 500000;  
      FD_ZERO(&readfds);  
      FD_SET(STDIN,&readfds);  
      /* 这里不关心写文件和异常处理文件描述符集合 */  
      select(STDIN+1, &readfds, NULL, NULL, &tv);  
      if (FD_ISSET(STDIN, &readfds)) printf("A key was pressed!");  

      else printf("Timed out.");  
     }  
     (程序3)  
     select()在被监视端口等待2.5秒钟以后,就从select返回

  • 0
    点赞
  • 2
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<p> 这是一门linuxc++通讯架构实战课程,针对c/c++语言已经掌握很熟并希望进一步深造以将来用c++在linux从事网络通讯领域/网络服务器开发和架构工作。<br /> <br /> 这门课程学习难度颇高但也有着极其优渥薪水(最少30K月薪,最高可达60-80K月薪),这门课程,会先从nginx源码分析和讲解开始,逐步开始书写属于自己高性能服务器框架代码,完善个人代码库,这些,将会是您日后能取得高薪重要筹码。 </p> <p> <br /> </p> <p> <span style="color:#E53333;">本课程原计划带着大家逐行写代码,但因为代码实在过于复杂和精细,带着写代码可能会造成每节课至少要4~5小时超长时间,所以老师会在课前先写好代码,主要时间花费在逐行讲解这些代码上,这一点望同学们周知。如果你觉得非要老师领着写代码才行话,老师会觉得你当前可能学习本门课程会比较吃力,请不要购买本课程,以免听不懂课程并给老师差评,差评也会非常影响老师课程销售并造成其他同学误解。</span> </p> <p> <br /> </p> <p> 这门课程要求您具备技能:<br /> (1)对c/c++语言掌握非常熟练,语言本身已经不是继续学习障碍,并不要求您一定熟悉网络或者linux;<br /> (2)对网络通讯架构领域有兴趣、勇于挑战这个高难度开发领域并期望用大量付出换取高薪;<br /> <br /> 在这门课程中,实现了一个完整项目,其中包括通讯框架和业务逻辑框架,浓缩总结起来包括如几点:<br /> (1)项目本身是一个极完整多线程高并发服务器程序;<br /> (2)按照包头包体格式正确接收客户端发送过来数据包, 完美解决收包时数据粘包问题;<br /> (3)根据收到不同来执行不同业务处理逻辑;<br /> (4)把业务处理产生结果数据包正确返回给客户端;<br /> <br /> 本项目用到主要开发技术和特色包括:<br /> (1)epoll高并发通讯技术,用到触发模式是epoll中水平触发模式【LT】;<br /> (2)自己写了一套线程池来处理业务逻辑,调用适当业务逻辑处理函数处理业务并返回给客户端处理结果;<br /> (3)线程之间同步技术包括互斥量,信号量等等;<br /> (4)连接池中连接延迟回收技术,这是整个项目中精华技术,极大程度上消除诸多导致服务器程序工作不稳定因素;<br /> (5)专门处理数据发送一整套数据发送逻辑以及对应发送线程;<br /> (6)其他次要技术,包括信号、日志打印、fork()子进程、守护进程等等; </p> <div> <br /> </div>
第1篇Linux网络开发基础 第1章Linux操作系统概述 2 1.1Linux发展历史 2 1.1.1Linux诞生和发展 2 1.1.2Linux名称由来 3 1.2Linux发展要素 3 1.2.1UNIX操作系统 4 1.2.2Minix操作系统 4 1.2.3POSIX标准 4 1.3Linux与UNIX异同 5 1.4操作系统类型选择和内核版本选择 5 1.4.1常见不同公司发行Linux异同 6 1.4.2内核版本选择 6 1.5Linux系统架构 7 1.5.1Linux内核主要模块 7 1.5.2Linux文件结构 9 1.6GNU通用公共许可证 10 1.6.1GPL许可证历史 10 1.6.2GPL自由理念 10 1.6.3GPL基本条款 11 1.6.4关于GPL许可证争议 12 1.7Linux软件开发可借鉴之处 12 1.8小结 13 第2章Linux编程环境 14 2.1Linux环境编辑器 14 2.1.1vim使用简介 14 2.1.2使用vim建立文件 15 2.1.3使用vim编辑文本 16 2.1.4vim格式设置 18 2.1.5vim配置文件.vimrc 19 2.1.6使用其他编辑器 19 2.2LinuxGCC编译器工具集 19 2.2.1GCC简介 19 2.2.2编译程序基本知识 21 2.2.3单个文件编译成执行文件 22 2.2.4编译生成目标文件 22 2.2.5多文件编译 23 2.2.6预处理 24 2.2.7编译成汇编语言 24 2.2.8生成和使用静态链接库 25 2.2.9生成动态链接库 26 2.2.10动态加载库 29 2.2.11GCC常用选项 31 2.2.12编译环境搭建 33 2.3Makefile文件简介 34 2.3.1一个多文件工程例子 34 2.3.2多文件工程编译 36 2.3.3Makefile规则 37 2.3.4Makefile中使用变量 39 2.3.5搜索路径 43 2.3.6自动推导规则 44 2.3.7递归make 44 2.3.8Makefile中函数 46 2.4用GDB调试程序 47 2.4.1编译可调试程序 48 2.4.2使用GDB调试程序 49 2.4.3GDB常用命令 52 2.4.4其他GDB 59 2.5小结 60 第3章文件系统简介 61 3.1Linux文件系统 61 3.1.1Linux文件内涵 61 3.1.2文件系统创建 62 3.1.3挂接文件系统 64 3.1.4索引节点inode 65 3.1.5普通文件 66 3.1.6设备文件 66 3.1.7虚拟文件系统VFS 68 3.2文件通用操作方法 72 3.2.1文件描述符 72 3.2.2打开创建文件open()、create()函数 72 3.2.3关闭文件close()函数 76 3.2.4读取文件read()函数 77 3.2.5写文件write()函数 79 3.2.6文件偏移lseek()函数 80 3.2.7获得文件状态fstat()函数 83 3.2.8文件空间映射mmap()函数 85 3.2.9文件属性fcntl()函数 88 3.2.10文件输入输出控制ioctl()函数 92 3.3socket文件类型 93 3.4小结 93 第4章程序、进程和线程 94 4.1程序、进程和线程概念 94 4.1.1程序和进程差别 94 4.1.2Linux环境进程 95 4.1.3进程和线程 96 4.2进程产生方式 96 4.2.1进程号 96 4.2.2进程复制fork() 97 4.2.3system()方式 98 4.2.4进程执行exec()函数系列 99 4.2.5所有用户态进程产生进程init 100 4.3进程间通信和同步 101 4.3.1半双工管道 101 4.3.2命名管道 107 4.3.3消息队列 108 4.3.4消息队列一个例子 114 4.3.5信号量 116 4.3.6共享内存 121 4.3.7信号 124 4.4Linux线程 127 4.4.1多线程编程实例 127 4.4.2Linux线程创建函数pthread_create() 129 4.4.3线程结束函数pthread_join()和pthread_exit() 129 4.4.4线程属性 130 4.4.5线程间互斥 132 4.4.6线程中使用信号量 133 4.5小结 136 第2篇Linux用户层网络编程 第5章TCP/IP协议族简介 138 5.1OSI网络分层介绍 138 5.1.1OSI网络分层结构 138 5.1.2OSI7层网络结构 139 5.1.3OSI参考模型中数据传输 140 5.2TCP/IP协议栈 141 5.2.1TCP/IP协议栈参考模型 141 5.2.2主机到网络层协议 143 5.2.3IP协议 144 5.2.4网际控制报文协议(ICMP) 146 5.2.5传输控制协议(TCP) 150 5.2.6用户数据报文协议(UDP) 154 5.2.7地址解析协议(ARP) 156 5.3IP地址分类与TCP/UDP端口 158 5.3.1因特网中IP地址分类 159 5.3.2子网掩码(subnetmaskaddress) 161 5.3.3IP地址配置 162 5.3.4端口 163 5.4主机字节序和网络字节序 163 5.4.1字节序含义 164 5.4.2网络字节序转换 164 5.5小结 166 第6章应用层网络服务程序简介 167 6.1HTTP协议和服务 167 6.1.1HTTP协议概述 167 6.1.2HTTP协议基本过程 168 6.2FTP协议和服务 170 6.2.1FTP协议概述 170 6.2.2FTP协议工作模式 172 6.2.3FTP协议传输方式 172 6.2.4一个简单FTP过程 173 6.2.5常用FTP工具 173 6.3TELNET协议和服务 174 6.3.1远程登录基本概念 174 6.3.2使用TELNET协议进行远程登录工作过程 174 6.3.3TELNET协议 174 6.4NFS协议和服务 176 6.4.1安装NFS服务器和客户端 176 6.4.2服务器设定 176 6.4.3客户端操作 177 6.4.4showmount命令 177 6.5自定义网络服务 177 6.5.1xinetd/inetd 178 6.5.2xinetd服务配置 178 6.5.3自定义网络服务 179 6.6小结 180 第7章TCP网络编程基础 181 7.1套接字编程基础知识 181 7.1.1套接字地址结构 181 7.1.2用户层和内核层交互过程 183 7.2TCP网络编程流程 184 7.2.1TCP网络编程架构 184 7.2.2创建网络插口函数socket() 186 7.2.3绑定一个地址端口对bind() 189 7.2.4监听本地端口listen 192 7.2.5接受一个网络请求accept() 194 7.2.6连接目标网络服务器connect() 199 7.2.7写入数据函数write() 200 7.2.8读取数据函数read() 201 7.2.9关闭套接字函数close() 201 7.3服务器/客户端简单例子 202 7.3.1例子功能描述 202 7.3.2服务器网络程序 203 7.3.3服务器读取和显示字符串 205 7.3.4客户端网络程序 205 7.3.5客户端读取和显示字符串 206 7.3.6编译运行程序 206 7.4截取信号例子 207 7.4.1信号处理 207 7.4.2信号SIGPIPE 208 7.4.3信号SIGINT 208 7.5小结 208[1]
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值