嵌入式Linux--网络编程基础

一 、网络基础知识

(一)、网络采取分层结构:

1.每一层实现不同的功能,对数据进行透明传输
2.网络每一层向上层提供服务,同时享受下一层的服务
模型图:
在这里插入图片描述

(二)、各层典型协议:

1.网络物理接口层:
MAC地址:48位地址,全球唯一,网络身份的标识
ARP/RARP:
ARP:IP地址------->MAC地址
RARP:MAC地址---------->IP地址

PPP协议:拨号协议(GPS/3G/4G)
2.网络层协议
IP (IPv4 · IPv6) (Internet Protocol) 网络之间互连的协议
ICMP (Internet Control Message Protocol )Internet 控制报文协议。它是TCP/IP 协议族的一个子协议,用于在IP 主机、路由器之间传递控制消息。举例:ping命令
3.传输层
TCP (Transmission Control Protocol )传输控制协议提供可靠的面向连接的服务,传输数据前须先建立连接,结束后释放。可靠的全双工信道。可靠、有序、无丢失、不重复。
UDP (User Datagram Protocol )用户数据报协议发送数据前无需建立连接,不使用拥塞控制,不保证可靠交付,最大努力交付。
4.应用层
FTP (File Transfer Protocol )文件传输协议<端口号21>减少或消除不同操作系统下处理文件的不兼容性。
HTTP (Hypertext Transfer Protocol )超文本传输协议 <端口号 80>, 面向事务的应用层协议。
SSH (Secure Shell )安全外壳协议

嵌入式相关:
NTP网络时钟管理
SNMP:简单网络管理(网络设备集中管理)
RTP/RTSP:传输音视屏协议(安防监控)

(三)、网络的分装和拆包

在这里插入图片描述

二、网络编程预备知识

1.socket

是一个编程接口
是一种特殊的文件描述符(可以使用read,write等文件io函数),一种网络资源
socket类型

流式套接字(SOCK_STREAM):唯一对应TCP
 提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。

数据报套接字(SOCK_DGRAM):唯一对应UDP
 提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。

原始套接字(SOCK_RAW):对应多种协议,使用ping可以直接穿透传输层
 可以对较低层次协议如IP、ICMP直接访问。
在这里插入图片描述

2.IP地址

Internet中的主机要与别的机器通信必须具有一个IP地址
IP地址为32位****(IPv4)或者128位(IPv6)
每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由
特殊IP:
局域网:192.XXX.XXX.XXX 10.XXX.XXX.XXX
广播:XXX.XXX.XXX.255 255.255.255.255(全网广播)
组播:224.XXX.XXX.XXX ~239.XXX.XXX.XXX

3.端口号

ip地址确认哪台机器,端口号确认哪个进程或者线程(数据传输给哪个任务)
保留端口:FTP 21, SSH 22 ,HTTP 80, HTTPS 469
1025-5000端口不建议使用
可以使用:5000~65535
TCP 和UDP端口是相互独立的

4.字节序

1.cpu访问多字节数据时存在字节序的问题(string字符串则不存在这个问题)
大端字节序:数据高位对应cpu内存地址高位
小端字节序:数据低位对应vpu内存地址高位
在这里插入图片描述
2.字节序转换相关函数
(1)主机字节序到网络字节序
  u_long htonl (u_long hostlong);
  u_short htons (u_short short);
(2)网络字节序到主机字节序
  u_long ntohl (u_long hostlong);
  u_short ntohs (u_short short);
3.IP地址转换
(1)将strptr所指的字符串转换成32位的网络字节序二进制值
  int_addr_t inet_addr(const char *strptr);
(2)将IPV4/IPV6的地址转换成binary格式
  int inet_pton(int af, const char *src, void *dst);
逆过程:inet_ntop()
参数:
  af:AF_INET(IPV4)AF_INET6(IPV6)
  src:IPV4点分十进制
  dst:转换后的结果

三、TCP编程

在这里插入图片描述

3.1 socket函数

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数:
 1.domain:

AF_INET      IPv4 Internet protocols                    ip(7)
AF_INET6     IPv6 Internet protocols                    ipv6(7)	
AF_UNIX      Local communication                        unix(7)
AF_LOCAL     Synonym for AF_UNIX
AF_NETLINK   Kernel user interface device               netlink(7)
AF_PACKET    Low-level packet interface                 packet(7)

 2.type
   SOCK_STREAM:流式套接字,唯一对应TCP
   SOCK_DGRAM:数据报套接字,唯一对应UDP
  SOCK_RAM:原始套接字
 3.protocol:一般填0,使用原始套接字需要填充
返回值:
RETURN VALUE
On success, a file descriptor for the new socket is returned. On er‐
ror, -1 is returned, and errno is set appropriately.

3.2 bind函数

SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd:通过socket拿到的文件描述符
addr:结构体变量的地址
struct sockaddr {
  sa_family_t sa_family;
  char sa_data[14];
};

具体要我们填充的数据结构体
struct sockaddr_in {
  sa_family_t sin_family;   /* address family: AF_INET /
  in_port_t sin_port;    /
port in network byte order /
  struct in_addr sin_addr;    /
internet address */
}

struct in_addr {
  uint32_t s_addr; /* address in network byte order */
};
addrlen:地址长度

3.3 listen()函数

SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);

参数:
backlog:表示三次握手阶段最多允许几个客户端连接,一般设置为5
return:
-1表示失败,0表示成功

3.4 accept()函数

SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

   int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:
addr:存放保存连接客户端的地址
addlen:存放保存连接客户端的地址长度
返回值:
On success, these system calls return a nonnegative integer that is a file descriptor for the accepted socket. On error, -1 is returned, errno is set appropriately, and addrlen is left unchanged.

3.5 connect()函数

SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

   int connect(int sockfd, const struct sockaddr *addr,
               socklen_t addrlen);

与bind相类似
client.c

 1 #include "net.h"
  2 
  3 int main(){
  4     int fd=-1;
  5     fd=socket(AF_INET,SOCK_STREAM,0);
  6     if(fd<0){
  7         perror("socket");
  8         exit(1);
  9     }
 10 
 11     struct sockaddr_in sin;
 12     sin.sin_family=AF_INET;
 13     sin.sin_port=htons(SERVE_POET);
 14 #if 0
 15     sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
 16 #else
 17     if((inet_pton(AF_INET,SERVE_ADDR,&sin.sin_addr.s_addr)!=1)){
 18         perror("inet_pton");
 19         exit(1);
 20     }
 21 #endif
 22 
 23     if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 24         perror("connect");
 25         exit(1);
 26     }
 27     char buf[BUFSIZE];
 28     while(1){
 29         bzero(buf,strlen(buf));
 30         printf(">");
 31         if((fgets(buf,32,stdin)==NULL)){
 32             perror("fgets");
 33             continue;
 34         }
 35         if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
 35         if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
 36             break;
 37         }
 38         write(fd,buf,strlen(buf));
 39     }
 40 
 41     close(fd);
 42 
 43 
 44 }

server.c

 1 #include "net.h"
  2 int main(){
  3 
  4     /*1.创建socket套接字(文件描述符)*/
  5     int fd=-1;
  6     if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
  7         perror("socket");
  8         exit(-1);
  9     }
 10     /*2.绑定bind*/
 11     /*2.1填充结构体数据*/
 12     struct sockaddr_in sin;
 13     bzero(&sin,sizeof(sin));
 14     sin.sin_family=AF_INET;
 15     sin.sin_port=htons(SERVE_POET);
 16 #if 1
 17    // sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
 18    /*优化1:能绑定任意服务器*/
 19    sin.sin_addr.s_addr=htonl(INADDR_ANY);
 20 #else
 21 
 22     if((inet_pton(AF_INET,SERVE_ADDR,(void*)&sin.sin_addr.s_addr)!=1)){
 23             perror("inet_pton");
 24             exit(1);
 25             }
 26 #endif
 27             /*2.2 bind*/
 28             if(( bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 29                 perror("bind");
 30                 exit(1);
 31 
 32                 }
 33                 /*3.利用listen函数将主动套接字变为被动套结字*/
 34                 if(listen(fd,BACKLOG)<0){
 35                 perror("listen");
36                 exit(1);
 37                 }
 38                 /*4.阻塞等待客户端连接*/
 39 #if 0
 40                 int newfd=-1;
 41                 newfd=accept(fd,NULL,NULL);
 42                 if(newfd<0){
 43                 perror("accept");
 44                 exit(1);
 45                 }
 46 #else
 47                 //优化2:获取连接客户端信息
 48                 int newfd=-1;
 49                 struct sockaddr_in cin;
 50                 socklen_t addrlen=sizeof(cin);
 51                 char clientaddr[16];
 52                 newfd=accept(fd,(struct sockaddr*)&cin,&addrlen);//无法实现多个客户端连接,因此需要多线程来解决
 53                 if(newfd<0){
 54                     perror("accept");
 55                     exit(1);
 56                 }
 57                 /*网络字序转换*/
 58                 if(!(inet_ntop(AF_INET,(void*)&cin.sin_addr.s_addr,clientaddr,sizeof(cin)))){
 59                     perror("inet_ntop");
 60                     exit(1);
 61                 }
 62                 printf("client addr:%s port:%d\n",clientaddr,ntohs(cin.sin_port));
 63 
 64 
 65 #endif
 66 
 67                 /*5.读写*/
 68                 char buf[BUFSIZE];
 69                 int ret=-1;
 70                 while(1){
 71                     bzero(buf,sizeof(buf));
 72                     do{
 73                         ret=read(newfd,buf,BUFSIZE-1);
 74 
 75                     }while(ret<0 && EINTR==errno);
 76 
 77                     if(ret<0){
 78                         perror("read");
 79                         exit(1);
 80                     }
 81 
 82                     printf("receive %s",buf);
 83 
 84                     if(!strncasecmp(STR_QUIT,buf,strlen(STR_QUIT))){
 85                         printf("Client is exiting\n!");
 86                         break;
 87                     }
 88                 }
 89                 close(newfd);
 90                 close(fd);
 91                 return 0;
 92 }

net.h

  1 #ifndef _NET_H_
  2 #define _NET_H_
  3 #include<stdio.h>
  4 #include<stdlib.h>
  5 #include<unistd.h>
  6 #include<string.h>
  7 #include<strings.h>
  8 #include <sys/types.h>          /* See NOTES */
  9 #include <sys/socket.h>
 10 #include <netinet/in.h>
 11 #include <netinet/ip.h> /* superset of previous */
 12 #include <arpa/inet.h>
 13 #include<errno.h>
 14 
 15 
 16 
 17 #define SERVE_POET 5001
 18 #define SERVE_ADDR "127.0.0.1"
 19 #define BACKLOG 5
 20 #define BUFSIZE 128
 21 #define STR_QUIT "quit"
 22 
 23 
 24 
 25 
 26 
 27 #endif
 28 

3.6 TCP并发服务器多线程/多进程

多线程服务器
server.c

1 #include<pthread.h>
  2 #include "net.h"
  3 void cli_data_handler(void* arg);
  4 int main(){
  5 
  6     /*1.创建socket套接字(文件描述符)*/
  7     int fd=-1;
  8     if((fd=socket(AF_INET,SOCK_STREAM,0))<0){
  9         perror("socket");
 10         exit(-1);
 11     }
 12     /*2.绑定bind*/
 13     /*2.1填充结构体数据*/
 14     struct sockaddr_in sin;
 15     bzero(&sin,sizeof(sin));
 16     sin.sin_family=AF_INET;
 17     sin.sin_port=htons(SERVE_POET);
 18 #if 1
 19     // sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
 20     /*优化1:能绑定任意服务器*/
 21     sin.sin_addr.s_addr=htonl(INADDR_ANY);
 22 #else
 23 
 24     if((inet_pton(AF_INET,SERVE_ADDR,(void*)&sin.sin_addr.s_addr)!=1)){
 25         perror("inet_pton");
 26         exit(1);
 27     }
 28 #endif
 29     /*2.2 bind*/
 30     if(( bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 31         perror("bind");
 32         exit(1);
 33 
 34     }
 35     /*3.利用listen函数将主动套接字变为被动套结字*/
36     if(listen(fd,BACKLOG)<0){
 37         perror("listen");
 38         exit(1);
 39     }
 40 
 41     int newfd=-1;
 42     while(1){
 43         pthread_t pid;
 44         struct sockaddr_in cin;
 45         socklen_t addrlen=sizeof(cin);
 46         char cli_addr[16];
 47         newfd=accept(fd,(struct sockaddr*)&cin,&addrlen);
 48         if(newfd<0){
 49             perror("accept");
 50             exit(1);
 51         }
 52 
 53         if(!inet_ntop(AF_INET,(void*)&cin.sin_addr.s_addr,cli_addr,addrlen)){
 54             perror("inet_ntop");
 55             exit(1);
 56         }
 57         printf("client addr=%s, port=%d\n",cli_addr,(int)ntohs(cin.sin_port));
 58         pthread_create(&pid,NULL,(void*)cli_data_handler,(void*)&newfd);
 59 
 60     }
 61     close(fd);
 62     return 0;
 63 }
 64 
 65 void cli_data_handler(void* arg){
 66     int newfd=*(int*)arg;
 67     printf("newfd=%d\n",newfd);
 68     int ret=-1;
 69     char buf[BUFSIZ];
 70     while(1){
 71         do{
 72             bzero(buf,BUFSIZE);
 73             ret=read(newfd,buf,BUFSIZE);
 74 
 75         }while(ret<0 && EINTR==errno);
 76         if(ret<0){
 77             perror("read");
 78             exit(1);
 79         }
 80         printf("%s",buf);
 81 
 82         if(!strncasecmp(STR_QUIT,buf,strlen(STR_QUIT))){
 83             printf("client is exiting\n");
 84             break;
 85         }
 86 
 87     }
 88     close(newfd);
 89 }

client.c

1 #include "net.h"
  2 void usage(char* s){
  3     printf("\n%s  server_ip,server_port",s);
  4     printf("\n server_ip:seriver ip address");
  5     printf("\n server_port:server port(>5000)\n\n");
  6 
  7 }
  8 int main(int argc,void* argv[]){
  9     int fd=-1;
 10     if(argc<3){
 11         usage(argv[0]);
 12         exit(1);
 13     }
 14     short port;
 15     fd=socket(AF_INET,SOCK_STREAM,0);
 16     if(fd<0){
 17         perror("socket");
 18         exit(1);
 19     }
 20 
 21     port=atoi(argv[2]);
 22     if(port<5000){
 23         usage(argv[0]);
 24         exit(1);
 25     }
 26 
 27     struct sockaddr_in sin;
 28     sin.sin_family=AF_INET;
 29     sin.sin_port=htons(port);
 30 #if 0
 31     sin.sin_addr.s_addr=inet_addr(SERVE_ADDR);
 32 #else
 33     if((inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)!=1)){
 34         perror("inet_pton");
 35         exit(1);
 36     }
 37 #endif
 38 
 39     if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 40         perror("connect");
 41         exit(1);
 42     }
 43     char buf[BUFSIZE];
 44     while(1){
 45         bzero(buf,strlen(buf));
 46         printf(">");
 47         if((fgets(buf,32,stdin)==NULL)){
 48             perror("fgets");
 49             continue;
 50         }
 51         if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
 52             break;
 53         }
 54         write(fd,buf,strlen(buf));
 55     }
 56 
 57     close(fd);
 58 
 59 
 60 }

TCP多进程
server.c

 1 #include<signal.h>
  2 #include"net.h"
  3 #include <sys/wait.h>
  4 void waitact(int sig){
  5     if(sig==SIGCHLD){
  6         waitpid(-1,NULL,WNOHANG);
  7     }
  8 
  9 }
 10 int main(){
 11     int fd=-1;
 12     fd=socket(AF_INET,SOCK_STREAM,0);
 13     if(fd<0){
 14         perror("socket");
 15         exit(0);
 16     }
 17     //信号机制。回收进程
 18     sigaction(SIGCHLD,(void*)&waitact,NULL);
 19 
 20     struct sockaddr_in sin;
 21     sin.sin_port=htons(SERVE_POET);
 22     sin.sin_family=AF_INET;
 23 #if 0
 24     if((inet_pton(AF_INET,SERVE_ADDR,&sin.sin_addr.s_addr))<0){
 25         perror("inet_pton");
 26         exit(0);
 27     }
 28 #else
 29     sin.sin_addr.s_addr=htonl(INADDR_ANY);
 30 #endif
 31     if((bind(fd,(struct sockaddr*)&sin,sizeof(sin)))<0){
 32         perror("bind");
 33         exit(1);
 34     }
 35 
36     if((listen(fd,BACKLOG))<0){
 37         perror("lsiten");
 38         exit(1);
 39     }
 40     int newfd=-1;
 41     struct sockaddr_in  cin;
 42     socklen_t addrlen=sizeof(cin);
 43     char clientaddr[16];
 44     while(1){
 45         bzero(clientaddr,sizeof(clientaddr));
 46         newfd=accept(fd,(struct sockaddr*)&cin.sin_addr.s_addr,&addrlen);
 47         if(newfd<0){
 48             perror("accept");
 49             exit(1);
 50         }
 51         pid_t pid;
 52         pid=fork();
 53         if(pid<0){
 54             perror("fork");
 55             exit(1);
 56         }else if(pid==0){
 57             close(fd);
 58             if((inet_ntop(AF_INET,(struct sockaddr*)&sin.sin_addr.s_addr,clientaddr,addrlen))<0){
 59                 perror("inet_ntop");
 60                 exit(1);
 61             }
 62             printf("client addr=%s,port=%d\n",clientaddr,ntohs(sin.sin_port));
 63             char buf[BUFSIZE];
 64             int ret=-1;
 65             while(1){
 66                 do{
 67                     bzero(buf,BUFSIZE);
 68                     ret=read(newfd,buf,BUFSIZE);
 69                 }while(ret<0 && EINTR==errno);
 70                 if(ret<0){
 71                     perror("read");
 72                     exit(1);
 73                 }
 74                 printf("receive data=%s",buf);
 75                 if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 76                     printf("client is exiting!\n");
 77                     break;
 78                 }
 79             }
 80             close(newfd);
 81         }else{//主线程
 82             close(newfd);
 83         }
 84     }
 85     close(fd);
 86 
 87 
 88 
 89 
 90 }

四、UDP编程

在这里插入图片描述

TCP拓展API:

send()/write():
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>

   ssize_t send(int sockfd, const void *buf, size_t len, int flags);

   ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);

参数:flag:
为0.则和write()函数一样
MSG_DONTWAIT:不阻塞
MSG_OOB:(out-of-band data)外带函数,tcp类型

recv()/read()
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>

   ssize_t recv(int sockfd, void *buf, size_t len, int flags);

   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);

参数: falgs
为0则和read一样
MSG_DONTWAIT:不阻塞
MSG_OOB:(out-of-band data)外带函数,tcp类型
MSG_PEEK:读取数据都是从头开始,而不移动读取位置

server.c

1 #include "net.h"
  2 
  3 int main(){
  4     int fd=-1;
  5     fd=socket(AF_INET,SOCK_DGRAM,0);
  6     if(fd<0){
  7         perror("socket");
  8         exit(0);
  9 
 10     }
 11 
 12     int b_reuse = 1;
 13     setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));
 14 
 15 
 16     struct sockaddr_in sin;
 17     sin.sin_family=AF_INET;
 18     sin.sin_port=htons(SERVE_POET);
 19     sin.sin_addr.s_addr=htonl(INADDR_ANY);
 20 
 21     if(bind(fd,(struct sockaddr*)&sin,sizeof(sin))<0){
 22         perror("bind");
 23         exit(0);
 24     }
 25 
 26     struct sockaddr_in cin;
 27     char buf[BUFSIZE];
 28     socklen_t addrlen=sizeof(cin);
 29     char cli_addr[16];
 30     printf("UDPserver is starting ......\n");
 31     while(1){
 32         bzero(buf,BUFSIZE);
 33         if(recvfrom(fd,buf,BUFSIZE-1,0,(struct sockaddr *)&cin,&addrlen)<0){
 34             perror("recvfrom");
 35             continue;
 36         }
 37 
 38         if(inet_ntop(AF_INET,(struct sockaddr*)&cin.sin_addr.s_addr,cli_addr,addrlen)<0){
 39             perror("inet_ntop");
 40             continue;
 41         }
 42         printf("client(%s:%d):receive data=%s\n",cli_addr,ntohs(cin.sin_port),buf);
 43 
 44         if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 45             printf("client(%s:%d) is exit!\n",cli_addr,ntohs(cin.sin_port));
 46         }
 47 
 48     }
 49     close(fd);
 50     return 0;
 51 
 52 }
                

client .c

1 #include "net.h"
  2 
  3 void usage(char* s){
  4     printf("\n%s:server_ip,server_port:\n",s);
  5     printf("server_ip:server ipaddress\n");
  6     printf("server_port:sever port(>5000)\n");
  7 
  8 }
  9 int main(int argc,void* argv[]){
 10     int fd=-1;
 11     fd=socket(AF_INET,SOCK_DGRAM,0);
 12     if(fd<0){
 13         perror("socket");
 14         exit(1);
 15     }
 16     if(argc<3){
 17         usage(argv[0]);
 18         exit(0);
 19     }
 20     int port;
 21     port=atoi(argv[2]);
 22     if(port<5000){
 23         usage(argv[0]);
 24         exit(1);
 25     }
 26 
 27     char buf[BUFSIZE];
 28     struct sockaddr_in sin;
 29     sin.sin_port=htons(port);
 30     sin.sin_family=AF_INET;
 31     if(inet_pton(AF_INET,argv[1],(void*)&sin.sin_addr.s_addr)<0){
 32         perror("inet_pton");
 33         exit(1);
 34     }
 35 
 36     while(1){
 37         bzero(buf,sizeof(buf));
 38         printf(">");
 39         if(fgets(buf,BUFSIZE,stdin)==NULL){
 40             perror("fgets");
 41             continue;
 42         }
 43 
 44         if(sendto(fd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin))<0){
 45             perror("sendto");
 46             continue;
 47         }
 48         if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 49             printf("client is exited!\n");
 50             break;
 51         }
 52 
 53     }
 54     close(fd);
 55 
 56 
 57 
 58     return 0;
 59 }

五、IO多路复用

linux下四种常见I/O模型:
阻塞I/O :最常用
非阻塞I/O:需要轮询
I/O多路复用:允许对多个I/O控制
信号驱动I/O:异步通信模型

5.1多路复用(select函数使用,解决阻塞的问题)

步骤:

1.fd_se集合,将关心的文件描述符加入到fd_set集合中
2.select函数阻塞监控
3.1个或者多个文件描述符有数据exit
4.判断哪个文件描述符有数据
5.依次对数据进行处理

相关函数介绍

1、 fd_set
void FD_CLR(int fd, fd_set *set);//将fd从集合中清除
int FD_ISSET(int fd, fd_set *set);//判断fd是否在集合中
void FD_SET(int fd, fd_set *set);//将fd加入到集合中
void FD_ZERO(fd_set *set);//集合初始化
2.select
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

nfds:maxfd+1
readfds:读集合
writerfds:写集合,一般为NULL
expectfds:异常处理集合(带外数据),一般为NULL
timerout:阻塞超时时间

notes:select函数前后,集合性质发生了变化,select是将关心的fd加入集合,select是有数据的fd保留了下来,因此需要用FD_ISSET判断进行相应的操作。
client.c

 1 #include"net.h"
  2 void usage(char*s){
  3     printf("\n%s: server_addr,server_port(>5000)\n",s);
  4 }
  5 int main(int argc,void *argv[]){
  6     int fd=-1;
  7     fd=socket(AF_INET,SOCK_STREAM,0);
  8     if(fd<0){
  9         perror("socket");
 10         exit(1);
 11     }
 12     if(argc<3){
 13         usage(argv[0]);
 14         exit(1);
 15     }
 16     int port=atoi(argv[2]);
 17     if(port<5000){
 18         usage(argv[0]);
 19         exit(1);
 20     }
 21 
 22     struct sockaddr_in sin;
 23     sin.sin_family=AF_INET;
 24     sin.sin_port=htons(port);
 25     if(inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)<0){
 26         perror("inet_pton");
 27         exit(1);
 28     }
 29 
 30     if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0){
 31         perror("connect");
 32         exit(1);
 33     }
 34 
 35     fd_set rset;
36     int maxfd=-1;
 37     struct timeval timeout;
 38     timeout.tv_sec=5;
 39     timeout.tv_usec=0;
 40     int ret=-1;
 41     char buf[BUFSIZE];
 42     while(1){
 43         bzero(buf,BUFSIZE);
 44         FD_ZERO(&rset);
 45         FD_SET(0,&rset);
 46         FD_SET(fd,&rset);
 47         maxfd=fd;
 48         select(maxfd+1,&rset,NULL,NULL,&timeout);
 49 
 50         if(FD_ISSET(0,&rset)){//从标准输入当中读取字符
 51             do{
 52                 ret=read(0,buf,BUFSIZE-1);
 53             }while(ret<0 && EINTR==errno);
 54 
 55             if(ret<0){
 56                 perror("read");
 57                 continue;
 58             }
 59             if(!ret)continue;//没有字符输入
 60 
 61             write(fd,buf,BUFSIZE);
 62             if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 63                 printf("client is exited!\n");
 64                 break;
 65             }
 66         }
 67         if(FD_ISSET(fd,&rset)){
 68             bzero(buf,BUFSIZE);
 69             do{
 70                 ret=read(fd,buf,BUFSIZE);
 71 
 72             }while(ret<0 && EINTR==errno);
 73             if(ret<0){
 74                 perror("read");
 75                 continue;
 76             }
 77             if(!ret)break;//服务器关闭
 78             printf("sever data:%s\n",buf);
 79             if(!strncasecmp(buf,STR_QUIT,strlen(STR_QUIT))){
 80                 printf("server is closed\n");
 81                 break;
 82             }
 83 
 84         }
 85 
 86     }
 87     close(fd);
 88 
 89 
 90 }

六、TCP IP协议

1.以太网头

在这里插入图片描述

2.IP头

在这里插入图片描述

3.tcp头

在这里插入图片描述

4.tcp握手过程分析

在这里插入图片描述

7网络编程扩展

1.网络信息检索

域名解析相关函数
SYNOPSIS
#include <netdb.h>
extern int h_errno;

   struct hostent *gethostbyname(const char *name);//域名解析

   #include <sys/socket.h>       /* for AF_INET */
   struct hostent *gethostbyaddr(const void *addr,
                                 socklen_t len, int type);

   void herror(const char *s);//报错提示

   const char *hstrerror(int err);

   /* System V/POSIX extension */
   struct hostent *gethostent(void);

       struct hostent {
           char  *h_name;            /* official name of host */
           char **h_aliases;         /* alias list */
           int    h_addrtype;        /* host address type */
           int    h_length;          /* length of address */
           char **h_addr_list;       /* list of addresses */
       }
       #define h_addr h_addr_list[0] /* for backward compatibility */

RETURN VALUE
The gethostbyname() and gethostbyaddr() functions return the hostent structure or a null pointer if an error occurs.
On error, the h_errno variable holds an error number. When non-NULL, the return value may point at static data, see the notes below.

 1 #include<netdb.h>
  2 #include "net.h"
  3 void usage(char* s){
  4     printf("\n%s  server_name,server_port",s);
  5     printf("\n server_name:seriver ip address");
  6     printf("\n server_port:server port(>5000)\n\n");
  7 
  8 }
  9 int main(int argc,void* argv[]){
 10     int fd=-1;
 11     if(argc<3){
 12         usage(argv[0]);
 13         exit(1);
 14     }
 15     short port;
 16     fd=socket(AF_INET,SOCK_STREAM,0);
 17     if(fd<0){
 18         perror("socket");
 19         exit(1);
 20     }
 21     
 22     port=atoi(argv[2]);
 23     if(port<5000){
 24         usage(argv[0]);
 25         exit(1);
 26     }
 27     //域名处理
 28     struct hostent *hs;
 29     if((hs=gethostbyname(argv[1]))==NULL){
 30         herror("gethostname");
 31         exit(1);
 32     }
 33     struct sockaddr_in sin;
 34     sin.sin_family=AF_INET;
 35     sin.sin_port=htons(port);
 36 #if 1
 37     sin.sin_addr.s_addr=*(uint32_t *)hs->h_addr;//域名转换
 38 #else
 39     if((inet_pton(AF_INET,argv[1],&sin.sin_addr.s_addr)!=1)){
 40         perror("inet_pton");
 41         exit(1);
 42     }
 43 #endif
 44 
 45     if((connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)){
 46         perror("connect");
 47         exit(1);
 48     }
 49     char buf[BUFSIZE];
 50     while(1){
 51         bzero(buf,strlen(buf));
 52         printf(">");
 53         if((fgets(buf,32,stdin)==NULL)){
 54             perror("fgets");
 55             continue;
 56         }
 57         if((strncasecmp(STR_QUIT,buf,strlen(STR_QUIT)))==0){
 58             break;
 59         }
 60         write(fd,buf,strlen(buf));
 61     }
 62 
 63     close(fd);
 64 
 65 
 66 }

2 网络属性设置

SYNOPSIS
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

   int getsockopt(int sockfd, int level, int optname,
                  void *optval, socklen_t *optlen);
   int setsockopt(int sockfd, int level, int optname,
                  const void *optval, socklen_t optlen);

level指定控制套接字的层次.可以取三种值:
1)SOL_SOCKET:通用套接字选项. (应用层)
2)IPPROTO_IP:IP选项. (网络层)
3)IPPROTO_TCP:TCP选项. (传输层)
optname指定控制的方式(选项的名称),我们下面详细解释 
在这里插入图片描述

举例:
允许地址快速重用
int reuse=1;
setsockopt(fd,SOL_SOCKET,SO_RESUSERADDR,&reuse,sizeof(int))

3 网络超时优化

方法一:
设置socket的属性 SO_RCVTIMEO

参考代码如下
struct timeval tv;

 tv.tv_sec = 5;   //  设置5秒时间
 tv.tv_usec = 0;

 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,  &tv,  
                   sizeof(tv));   //  设置接收超时
  recv() / recvfrom()    //   从socket读取数据

方法二:
用select检测socket是否’ready’

参考代码如下
struct fd_set rdfs;
struct timeval tv = {5 , 0}; // 设置5秒时间

FD_ZERO(&rdfs);
FD_SET(sockfd, &rdfs);

if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0)   // socket就绪
{
      recv() /  recvfrom()    //  从socket读取数据
}

方法三:
设置定时器(timer), 捕捉SIGALRM信号

参考代码如下
void handler(int signo) { return; }

  struct sigaction  act;
  sigaction(SIGALRM, NULL, &act);
  act.sa_handler = handler;
  act.sa_flags &= ~SA_RESTART;
  sigaction(SIGALRM, &act, NULL);
  alarm(5);
  if (recv(,,,) < 0) ……

心跳检测:

4 广播发送

sender:UDP客户端
receiver:UDP服务端
广播发送:只需要修改客户端即可
加上如下代码:
int so_br=1;
setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&so_br,sizeof(int));

5 多播

组播接收
1.创建用户数据报套接字
2.加入多播组
3.绑定本机IP地址和端口
4.绑定的端口必须和发送方指定的端口相同
5.等待接收数据

加入多播组

struct ip_mreq mreq;
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr(“235.10.10.3”);
mreq.imr_interface.s_addr = htonl(INADDR_ANY);

setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
struct ip_mreq
{
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
};

7 UINX域套接字(本地进程通信)

1.进程间通信方式:
管道,消息队列,共享内存,信号,unix域套接字
易用性:消息队列>unix套接字>管道>共享内存
效率性:共享内存>unix套接字>管道>消息队列
2.异步通信
信号
2.同步和互斥
信号量

unix域套接字
和普通tcp和udp套接字流程是一样的,唯一区别就是套接字类型不同,适用于本地通信
socket(AF_UNIX/AF_LOACL,SOCK_STRAM,0);
bind地址结构的需要改变:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX /
char sun_path[108]; /
Pathname */
};
还需要利用access()函数判断文件是否存在?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值