网络编程

asource insight.f7搜函数

一、网络介绍    
协议:规则
tcp/ip模型4层: 
应用层{http超文本传输协议 ftp文件传输协议 telnet远程登录ssh安全外壳协议stmp简单邮件发送 pop3收邮件} 
传输层{tcp传输控制协议,udp用户数据包协议} 
网络层{ip网际互联协议 icmp网络控制消息协议 igmp网络组管理协议} 
网络接口层{arp地址转换协议,rarp反向地址转换协议,mpls多协议标签交换}



IP协议:网络层。IP报文协议。包地址,校验。
ping:ICMP协议。

传输层:你想以什么方式把数据给对方。

应用层:浏览器走的http协议。
ssh协议。

XMPP协议 即时通信协议  腾讯没有用

QICQ:
国外的聊天用的开源的。
能够做协议

每一个计算机有一个ip。端口的背后是进程。

用户自己定义的端口通常大于1024.
牛逼的留给1024以下。

必须要学会TCP、UDP。用来实现上层协议。

需要进行三次握手的协议。tcp
每次我都把数据丢给你。upd

积极确认重传:积极的概念。
TCP:网络稳定不会重复建立连接
什么时候用UDP:优酷视频,不需要建立连接的情况。一般是视频,摄像头。

二、网络相关概念   
套接口,套接字(socket)
在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一一对应关系。
short 短整形,2个字节;long 长整形,4个字节;unsigned short,无符号短整型,2个字节;unsigned long,无符号长整形,4个字节。

struct sockaddr_in {                          //旧的版本是struct sockaddr,这个版本可以兼容旧版本。
  short int sa_family;                    //地址族。如果是IPv4协议,就写:AF_INET;如果是IPv6协议,就写:AF_INET6。    
  unsigned short int sin_port;        //端口号。如果端口是2000,不能直接赋2000的,因为网络字节序的问题。         
  struct in_addr sin_addr;              //IP地址 
  unsigned char sin_zero[8];         //填充0 以保持与struct sockaddr同样大小
};

struct in_addr {                 
  unsigned long int  s_addr;         //32位IPv4地址,网络字节序的。无符号长整形,4个字节。
}; 
 
数据存储优先顺序的转换(大小端的问题):
网络字节序:在网络上是大端存储的,但是在自己的计算机上大小端都有可能,如Intel是小端存储的。
如数据实际是0x12345678,则在网络中存储的是0x12345678,Intel内存中是:0x78563412。
要把主机字节序和网络字节序相互对应起来,需要对这两个字节存储优先顺序进行相互转化。这里用到四个数:
htons()  host to net short   //如果主机是大端存储的,就不变,如果主机是小端存储的,就反过来
ntohs()  net to host short
htonl()  host to net long
ntohl()  net to host long

例:
int main(){
        unsigned short i=0x1234;              //定义i=0x1234,在主机上存的是:0x3412
        unsigned short j;
        unsigned short k;
        j=htons(i);                                   //host to net short 把主机转成网络格式。
        printf("j=%x\n",j);                       //
        k=ntohs(j);                                  //net to host short 把网络转成主机格式。
        printf("k=%x\n",k);
        return 0;
}

地址格式转换:
a:点分十进制
n:net
int inet_aton(const char *straddr, struct in_addr *addrptr);    //将点分十进制的straddr 转化为:32位的IPV4的网络字节序,保存到addrptr中 
char *inet_ntoa(struct in_addr inaddr);                                //将32位的网络字节序,转化为点分十进制字符串。
in_addr_t inet_addr(const char *straddr);                             //in_addr_t 就是unsigned long int ,代表s_addr,即struct in_addr结构体中的参                                                                                                数。所以该句直接把点分十进制转换为32位网络字节序的IP地址。
const char *inet_ntop(int family, const void *src, char *dst, socklen_t len); //family参数为AF_INET,表示是IPv4协议;为AF_INET6,表示                                                                                             IPv6协议。把网络字节序转换为点分十进制字符串。与inet_ntoa类似。但是                                                                                              inet_ntoa()只能转换成32位的网络字节序(即IPV4地址),而这个函数可以转换                                                                                            IPV4或IPV6.
int inet_pton(int family, const char *src, void *dst); 


例:
int main(int argc,char* argv[]){
        if(argc!=2){
                printf("error args\n");
                return -1;
        }
        struct in_addr addr;          
        int ret;
        ret=inet_aton(argv[1],&addr);                                    //将argv[1]转化为网络字节序,存到结构体addr中。如:192.168.4.155  
        if(ret!=1){                                                                //成功返回1   
                perror("inet_aton");
                return -1;
        }
        printf("%x\n",addr.s_addr);                                       //打印出:9b04a8c0.  c0就是192,a8就是168,9b就是155。因为是网络是大端                                                                                         存储的,所以这么存。
        printf("%s\n",inet_ntoa(addr));
        printf("inet_addr=%x\n",inet_addr(argv[1]));              //点分十进制转换为32位网络字节序的IP地址
        return 0;
}

点分十进制转换为IPV6的


域名地址转换  
域名,如:www.baidu.com。域名解析服务器。买域名,需要花钱。一个域名可以对应多个IP地址。

struct hostent* gethostbyname(const char* hostname);         //通过域名得到地址   

struct hostent{
  char *h_name;            //正式主机名。 域名也有别名,www.a.shifen.com是百度的真实域名。                         
  char **h_aliases;        //主机别名。二级指针,常用于char * buf[],是一个指针数组                         
  int h_addrtype;           //主机IP地址类型,IPv4为AF_INET。                           
  int h_length;               //主机IP地址字节长度,对于IPv4是4字节,即32位。              
  char **h_addr_list;     //主机的IP地址列表,因为一个域名可以有多个IP地址。二级指针,常用语字符指针数组。                 

例:通过域名得到地址
int main(int argc,char* argv[]){
        if(argc !=2){
                printf("error args\n");
                return -1;
        }
        struct hostent *p;
        p=gethostbyname(argv[1]);                        //输入域名,通过域名对应域名的IP地址等信息,返回给指针p。    
        if(p==NULL){
                perror("gethostbyname");
                return -1;
        }
        printf("h_name=%s\n",p->h_name);           //打印出正式主机名。
        char str[32]={0};
        char *ptr;
        for(ptr=*(p->h_aliases);ptr!=NULL;){       //p->h_aliases是二级指针。*(p->h_aliases)等于*((p->h_aliases)+0)等于(p->h_aliases)[0]。   
                printf("ptr=%s\n",ptr);                      //打印出主机别名。
                (p->h_aliases)++;                             //(p->h_aliases)+n 指向一维数组(p->h_aliases)[n]
                ptr=*(p->h_aliases);
        }
        ptr=*(p->h_addr_list);
        while(ptr!=NULL){
                memset(str,0,sizeof(str));
                inet_ntop(p->h_addrtype,ptr,str,sizeof(str));    //p->h_addrtype为IP地址的类型。将网络字节序转换为点分十进制。ptr是要转换的                                                                                   字符串,str存储转化后的点分十进制。
                printf("str=%s\n",str);
                (p->h_addr_list)++;
                ptr=*(p->h_addr_list);
        }
        return 0;
}

标准C:是一个标准,需要代码实现。
glibc:linux下,开源的实现了标准C的代码。libc.so是glibc使用的库。
操作系统提供系统调用。
中间层使用系统调用。

SOCKET编程:TCP通信      
通过socket接口TCP及UDP通信。TCP/UDP是协议。

                                 
int socket(int domain,int type,int protocol);   //生成一个套接口描述符。domain:为AF_INET表示Ipv4网络协议,为AF_INET6表示IPv6协议                                                                      type为tcp表示SOCK_STREAM,为udp表示SOCK_DGRAM。protocol指定socket所使用的传                                                                     输协议编号。通常为0.
sockaddr结构体:
struct sockaddr_in{                                    //常用的结构体。sockaddr结构会因使用不同的socket domain而有不同结构定义  
  unsigned short int sin_family;              //地址族。IPV4为:AF_INET 
  uint16_t sin_port;                               //为使用的port编号。一般1024以上自己使用。
  struct in_addr sin_addr;                       //为IP地址 
  unsigned char sin_zero[8];                   //未使用
}; 
struct in_addr { 
  uint32_t s_addr;                                  //32位IPv4地址,网络字节序的。无符号长整形,4个字节。
};

int bind(int sockfd,struct sockaddr * my_addr,int addrlen); //my_addr为结构体指针变量。addrlen是sockaddr的结构体长度。通常是计                                                                                                sizeof(struct sockaddr) 。
int listen(int sockfd,int backlog);                 //backlog为同时能处理的最大连接要求。一个端口可以同时有多个客户端来连接。
int accept(int s,struct sockaddr * addr,int * addrlen);    //s即sfd。addr为结构体指针变量,和bind的结构体是同种类型的,系统会把远                                                                                      主机的信息(远程主机的地址和端口号信息)保存到这个指针所指的结构体中。成                                                                                      功则返回新的socket处理代码new_fd,失败返回-1回值。之后客户端通过new_fd来通                                                                                  信。
int recv(int sockfd,void *buf,int len,unsigned int flags);     //sockfd为前面accept的返回值.即new_fd,也就是新的套接字。buf表示缓冲区,                                                                                         len表示缓冲区的长度.
int send(int s,const void * msg,int len,unsigned int flags); //用新的套接字发送数据给指定的远端主机.s为前面accept的返回值.即new_fd.                                                                                           msg一般为常量字符串,len表示长度。
int close(int fd);
int connect(int sockfd,struct sockaddr * serv_addr,int addrlen);   //sockfd为前面socket的返回值,即sfd。serv_addr为结构体指针变量,存储                                                                                                着远程服务器的IP与端口号信息。addrlen表示结构体变量的长度。
例一:使用socket接口实现TCP通信  
//使用socket接口实现tcp通信的服务器端  
int main(int argc,char** argv){
        if(argc!=3){
                printf("error args\n");
                return -1;
        }
        int sfd=socket(AF_INET,SOCK_STREAM,0);   //生成套接口描述符sfd。
        if(-1==sfd){
                perror("socket");
                return -1;
        }      
        printf("sfd=%d\n",sfd);                             //新建的从3开始  
        struct sockaddr_in ser;                              //socket信息数据结构
        memset(&ser,0,sizeof(ser));
        ser.sin_family=AF_INET;                       //IPV4.
        ser.sin_port=htons(atoi(argv[2]));            //主机转网络,端口。先转成数字。
        ser.sin_addr.s_addr=inet_addr(argv[1]);  //IP地址。
        int ret;
        ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr)); //给sfd绑定IP地址和端口                                                                                                         号,&ser为结构体指针。
        if(-1==ret){
                perror("bind");
                return -1;
        }
        ret=listen(sfd,10);                                     //监听。最大连接数为10.
        if(-1==ret){
                perror("listen");
                return -1;
        }
        printf("listen success\n");
        struct sockaddr_in client;
        memset(&client,0,sizeof(client));
        int addrlen=sizeof(struct sockaddr);
        int new_fd=accept(sfd,(struct sockaddr*)&client,&addrlen); //接受连接请求 
        if(-1==new_fd){
                perror("accept");
                return -1;
        }
        printf("new_fd=%d\n",new_fd);
        printf("client IP=%s,client port=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        char buf[128]={0}; 
        ret=recv(new_fd,buf,sizeof(buf),0);       //接收 
        if(-1==ret){
                perror("recv");
                return -1;
        }
        printf("%s\n",buf);
        ret=send(new_fd,"I am server",11,0);    //发送 
        if(-1==ret){
                perror("send");
                return -1;
        }
        return 0;
}
//使用socket接口实现tcp通信的客户端  
int main(int argc,char** argv)//argv[1,2]存放服务器的地址和端口
        if(argc !=3){
                printf("error args\n");
                return -1;
        }
        int sfd=socket(AF_INET,SOCK_STREAM,0);
        if(-1==sfd){
                perror("socket");
                return -1;
        }
        struct sockaddr_in ser;                  //保存服务器端的信息 
        memset(&ser,0,sizeof(ser));
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi(argv[2]));                
        ser.sin_addr.s_addr=inet_addr(argv[1]);
        int ret;
        ret=connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
        if(-1==ret){
                perror("connect");
                return -1;
        }
        char buf[128]={0};
        ret=send(sfd,"I am client",11,0);  //客户端通信的时候用一个
        if(-1==ret){
                perror("send");
                return -1;
        }
        ret=recv(sfd,buf,sizeof(buf),0);
        if(-1==ret){
                perror("recv");
                return -1;
        }
        printf("%s\n",buf);
        return 0;
}


















注意:1.客户端只有一个sfd.clint的端口是系统随机打开的一个端口。
           2.自己的机子上的两个进程通信还是用之前学的共享内存,管道什么的。
   3.客户端的端口是系统自动分配的。

例子二:如何实现多个客户端连接服务器  
要求:服务器只有一个线程:
     1.主线程能够监听客户端的连接请求;
     2.能够接受已经连接的客户端发送来的数据。
     3.已经连接的客户端发送某个数据,回复相同数据给它。
客户端:
     1.不断开
     2.可以说话给服务端
//服务器端:一个服务器能够同时监听多个客户端请求
#define NUM 10
int main(int argc,char** argv){
        if(argc!=3){
                printf("error args\n");
                return -1;
        }
        int sfd=socket(AF_INET,SOCK_STREAM,0);                      
        if(-1==sfd){
                perror("socket");
                return -1;
        }
        struct sockaddr_in ser;
        memset(&ser,0,sizeof(ser));
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi(argv[2]));    
        ser.sin_addr.s_addr=inet_addr(argv[1]);
        int ret;
        ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));       //绑定端口和IP地址
        if(-1==ret){
                perror("bind");
                return -1;
        }
        ret=listen(sfd,NUM);
        if(-1==ret){
                perror("listen");
                return -1;
        }
        printf("listen success\n");
  
        int addrlen=sizeof(struct sockaddr);
        fd_set rdset;
        int new_fd[NUM]={0};
        int i=0;
        int j;
        fd_set tmpset;
        FD_SET(sfd,&tmpset);          //在tmpset中增加一个sfd  
        char buf[128]={0};
        while(1){
                FD_ZERO(&rdset);
                memcpy(&rdset,&tmpset,sizeof(rdset));
                ret=select(NUM+4,&rdset,NULL,NULL,NULL); //监听rdset中的元素,因为新 
                                                              建的第一个为sfd为3,listen最大的链接数为NUM.
                if(ret >0){
                       if(FD_ISSET(sfd,&rdset)){      //如果有新连接申请 
                                new_fd[i]=accept(sfd,(struct sockaddr*)&client,&addrlen);
                                if(-1==new_fd[i]){
                                        perror("accept");
                                        return -1;
                                }
                                printf("client IP=%s,client port=%d\n",inet_ntoa(client.sin_addr),
                                                                                              ntohs(client.sin_port));
                                FD_SET(new_fd[i],&tmpset);       //放到tmpset集合中 
                                i++;
                        }
                        j=0;
                        while(new_fd[j]!=0){
                                if(FD_ISSET(new_fd[j],&rdset)){  //如果有客户端说话
                                        memset(buf,0,sizeof(buf));
                                        ret=recv(new_fd[j],buf,sizeof(buf),0);
                                        if(ret >0){
                                                printf("%d=%s\n",new_fd[j],buf);
                                                int ret1=send(new_fd[j],buf,strlen(buf),0);
                                                if(-1==ret1){
                                                        perror("send");
                                                        return -1;
                                                }
                                        }else if(ret ==0){                     //如果客户端断开连接
                                                close(new_fd[j]);
                                                FD_CLR(new_fd[j],&tmpset);  //把该客户移除集合中 
                                        }else{
                                                exit(-1);
                                        }
                                }      
                                j++;
                        }
                }
        }              
        return 0;
}
//客户端   
int main(int argc,char** argv){
        if(argc !=3){
                printf("error args\n");
                return -1;
        }
        int sfd=socket(AF_INET,SOCK_STREAM,0);
        if(-1==sfd){
                perror("socket");
                return -1;
        }
        struct sockaddr_in ser;
        memset(&ser,0,sizeof(ser));
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi(argv[2]));//一定要用htons
        ser.sin_addr.s_addr=inet_addr(argv[1]);
        int ret;
        ret=connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
        if(-1==ret){
                perror("connect");
                return -1;
        }
        char buf[128]={0};
        while(1){
                memset(buf,0,sizeof(buf));
                ret=read(0,buf,sizeof(buf));           //读标准输入 
                if(ret <=0){
                        break;
                }
                ret=send(sfd,buf,strlen(buf)-1,0);
                if(-1==ret){
                        perror("send");
                        return -1;
                }
                memset(buf,0,sizeof(buf));
                ret=recv(sfd,buf,sizeof(buf),0);
                if(-1==ret){
                        perror("recv");
                        return -1;
                }
                printf("%s\n",buf);
        }
        return 0;
}































注意:这是用select实现的,在epoll一节中,会用epoll实现。  

SOCKET编程:UDP通信      
udp都是一个sfd。tcp中的可以写NULL。upd必须要拿出来。
必须先客户端往服务器端发数据,这样服务器端才能拿到客户端的ip.   
                   
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen); //读到buf中,读len个字节。flags=0.form保存                                                                                                                                发送过来一方的信息。fromlen=sizeof(struct sockaddr)
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);//发送msg,发送的长度为len,flags=0,to是                                                                                                                                    发送给谁。tolen=sizeof(struct sockaddr)   
例一:使用socket接口实现UDP通信  
//使用socket接口实现udp通信的服务器端    
int main(int argc,char** argv){
        if(argc!=3){
                printf("error args\n");
                return -1;
        }
        int sfd=socket(AF_INET,SOCK_DGRAM,0);   //UDP  
        if(-1==sfd){
                perror("socket");
                return -1;
        }      
        printf("sfd=%d\n",sfd);
        struct sockaddr_in ser;
        memset(&ser,0,sizeof(ser));
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi(argv[2]));
        ser.sin_addr.s_addr=inet_addr(argv[1]);
        int ret;
        ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr)); //绑定IP和端口  
        if(-1==ret){
                perror("bind");
                return -1;
        }
        struct sockaddr_in client;                                      //定义client用于保存客户端的信息 
        memset(&client,0,sizeof(client));
        int addrlen=sizeof(client);
        char buf[128]={0};
        ret=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&addrlen); //服务器端必须先接收,放到buf中,读sizeof(buf)个字节,对方的信息放到client中 
        if(-1==ret){
                perror("recvfrom");
                return -1;
        }
        printf("IP=%s,PORT=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        printf("%s\n",buf);
        ret=sendto(sfd,"Server",6,0,(struct sockaddr*)&client,sizeof(struct sockaddr));                                                                                       //往对方写"Server",6个字节,0,发送给client。
        if(-1==ret){
                perror("sendto");
                return -1;
        }
        close(sfd);
        return 0;
}
//使用socket接口实现udp通信的客户端    
int main(int argc,char** argv){
        if(argc!=3){
                printf("error args\n");
                return -1;
        }
        int sfd=socket(AF_INET,SOCK_DGRAM,0);
        if(-1==sfd){
                perror("socket");
                return -1;
        }      
        printf("sfd=%d\n",sfd);
        struct sockaddr_in ser;
        memset(&ser,0,sizeof(ser));
        ser.sin_family=AF_INET;
        ser.sin_port=htons(atoi(argv[2]));//一定要用htons
        ser.sin_addr.s_addr=inet_addr(argv[1]);
        int ret;
        ret=sendto(sfd,"I am client",11,0,(struct sockaddr*)&ser,sizeof(struct sockaddr));                           //先发送
        if(-1==ret){
                perror("sendto");
                return -1;
        }
        struct sockaddr_in client;
        memset(&client,0,sizeof(client));
        int addrlen=sizeof(client);
        char buf[128]={0};
        ret=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&addrlen);
        if(-1==ret){
                perror("recvfrom");
                return -1;
        }
        printf("SERVER IP=%s,PORT=%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        printf("%s\n",buf);
        close(sfd);
        return 0;
}




注意:1.必须客户端先向服务器端发送数据,这样服务器端才能知道是谁发来的,以及向谁发。
   2.



单播、广播、多播(组播).

组播:分组。如192.168.4.10-192.168.4.20是一个组


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值