zoukankan      html  css  js  c++  java
  • UC编程之网络通信(TCP/UDP)

    网络常识 

    OSI 7层模型(人机交互)

      物理层、数据链路层、网络层、传输层、会话层、表现层、应用层

    常见协议:

    tcp/udp/ip/ftp/http...

     

    IP地址--就是计算机在网络中的地址,是一个32位的整数(IPV4),目前也有IPV6

    IP地址在计算机中,以一个整数格式保存。因此IP地址在底层的描写方式:8位16进制。点分10进制是人类描述IP地址的主要方式。每个字节计算一个10进制的整数,中间用 “.”隔开。

       192.168.0.20(点分十进制0-255)

    ==0xC0 A8 00 14816进制)

    IP地址分为A B C D 4类。

    子网掩码--判断计算机是不是在一个局域网上

    166.111.160.1166.111.161.45

    子网掩码:255.255.254.0

    166.111.160.1

    255.255.254.0  (位与)

    --------------

    166.111.160.0

     

    166.111.161.45

    255.255.254.0  (位与)

    --------------

    166.111.160.0

    结论:166.111.160.1166.111.161.45在同一个局域网

     

    IP地址只能定位计算机,但没有访问权限。

    端口会开放访问的权限。端口可以用来定位计算机中的某个进程。

    网络编程必须提供IP地址和端口号。

    端口号是unsigned short,范围:0-65535

    0-1023 固有端口(不推荐使用)计算机预留

    1024-48XX 程序员使用的端口  安装某些程序也会占用,但很少

    48XX - 65535 不建议用,不稳定

     

    MAC地址,物理地址,是网卡的物理地址。IP地址和MAC地址绑定在一起。

    字节次序--计算机在存储整数时,有从高到低 和 从低到高之分,叫字节次序。字节次序在计算机中不确定,在网络传输的过程中是固定的。

    因此,对于端口,需要本地格式和网络格式之间的转换。

    域名:俗称网址,就是IP地址的助记,通过域名解析服务器 网址解析成IP地址完成访问。

     

    网络编程--Windows/Unix 都支持

    socket编程

    socket--插座、套接字(IP + 端口)

    关于socket编程的一些函数和常识

    socket编程分为:一对一  和 一对多

    一对一:

    socket通信包括本地通信(ipc)和网络通信

    一对一通信模型:

    服务器端编程步骤:

     1 创建一个socket,使用socket()

       int socket(int domain,int type,int protocol)

        domain--域,用来选择协议

        PF_UNIX  PF_LOCAL  PF_FILE 本地通信

        PF_INET  网络通信

        PF_INET6 网络通信(IPV6)

        注:PF也可以换成AF

       type--用来选择通信类型

        SOCK_STREAM --数据流,针对TCP协议

        SOCK_DGRAM --数据报,针对UDP协议

       protocol--本来应该用来指定协议,但没用了,因为协议已经被前2个参数指定,给 0即可

       返回socket描述符,失败返回-1

     2 准备通信地址

       关于通信地址有3个结构体:

       struct sockaddr{

         int sa_family;//协议

         char sa_data[];//地址

       }

       这个结构sockaddr不被真正使用,只是用来做相关函数的参数(不存数据)

      本地通信使用结构体:

      #include<sys/un.h> 

      struct sockaddr_un{

        int sun_family;//协议

        char sun_path[];//socket文件的路径

       }

      网络通信使用结构体:

      #include<netinet/in.h>

      struct sockaddr_in{

       int sin_family;//协议

       short port;//端口号

       struct in_addr sin_addr;//IP地址

      }

     3 绑定函数bind()

       bind(int sockfd,sockaddr,length)

     4 通信(read/write)

     5 关闭 close(sockfd)

    客户端编程步骤:

      与服务器端基本一样,除了第三步,第三步使用connect(),参数与bind一样

    注:服务器和客户端数据交互时,读写必须必须保持一致性(一边读,另一方写)

    查看本机IP地址的命令:

    Windows --ipconfig

    Unix -- ifconfig

    whereis 可以查看命令所在的目录

    ping IP地址 可以检测网络是否畅通

    本地通信实例:

        服务器端:

          1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<string.h>

          4 #include<unistd.h>

          5 #include<sys/socket.h>

          6 #include<sys/un.h>

          7 

          8 int main(){

          9  int sockfd = socket(PF_UNIX,SOCK_DGRAM,0);

         10  if(sockfd == -1) perror("socket"),exit(-1);

         11  struct sockaddr_un addr;

         12  addr.sun_family = PF_UNIX;//socket第一个参数保持一致

         13  strcpy(addr.sun_path,"a.sock");

         14  int res = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));

         15  if(res == -1) perror("bind"),exit(-1);

         16  printf("bind ok! ");

         17  char buf[100]={};

         18  res = read(sockfd,buf,sizeof(buf));

         19  printf("读了%d字节k,内容%s ",res,buf);

         20  close(sockfd);

         21  return 0;

         22 }

     

    客户端:

          1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<string.h>

          4 #include<unistd.h>

          5 #include<sys/socket.h>

          6 #include<sys/un.h>

          7 

          8 int main(){

          9   int sockfd = socket(PF_UNIX,SOCK_DGRAM,0);

         10   if(sockfd == -1) perror("socket"),exit(-1);

         11   struct sockaddr_un addr;

         12   addr.sun_family = PF_UNIX;

         13   strcpy(addr.sun_path,"a.sock");

         14   int res = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));

         15   if(res == -1) perror("connect"),exit(-1);

         16   printf("connect ok! ");

         17   write(sockfd,"hello",5);

         18   close(sockfd);

         19   return 0;

         20  }

    网络通信:

         服务器端:

          1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<string.h>

          4 #include<unistd.h>

          5 #include<sys/socket.h>

          6 #include<netinet/in.h>

          7 

          8 int main(){

          9  int sockfd = socket(PF_INET,SOCK_DGRAM,0);

         10  if(sockfd == -1) perror("socket"),exit(-1);

         11  struct sockaddr_in addr;

         12  addr.sin_family = PF_INET;

         13  addr.sin_port =htons(2222);//端口

         14  addr.sin_addr.s_addr =inet_addr("192.168.13.73");//服务器ip地址

             //inet_addr点分十进制转换成整数ip

         15  int res = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));

         16  if(res == -1) perror("bind"),exit(-1);

         17  char buf[100]={};

         18  res =read(sockfd,buf,100);

         19  printf("读到了res=%d字节,内容是%s ",res,buf);

         20  return 0;

         21 }

    客户端:

          1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<string.h>

          4 #include<unistd.h>

          5 #include<sys/socket.h>

          6 #include<netinet/in.h>

          7 

          8 int main(){

          9  int sockfd = socket(PF_INET,SOCK_DGRAM,0);

         10  if(sockfd == -1) perror("socket"),exit(-1);

         11  struct sockaddr_in addr;

         12  addr.sin_family = PF_INET;

         13  addr.sin_port =htons(2222);

         14  addr.sin_addr.s_addr =inet_addr("192.168.13.73");/服务器ip地址

             //inet_addr点分十进制转换成整数ip

             //服务器端和客户端的ip都是服务器ip地址

         15  int res = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));

         16  if(res == -1) perror("bind"),exit(-1);

         17  write(sockfd,"hello",5);

         18  return 0;

         19 }

     

    TCP 一对多编程步骤:

    服务器端:

     1 调用socke()创建socket,type必须是SOCK_STREAM(保证使用TCP模式)

     2 准备通信地址sockaddr_in

     3 bind()绑定

     4 监听 listen(),为accept()做准备

     5 等待客户端的连接accept()

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

       参数sockfd就是第一步的socket

       参数addr就是链接上来的客户端的通信地址

       参数len是一个传入传出参数,传入通信地址的大小,传出客户端通信地址  的大小

      (要准备一个sockaddr_in 结构来存放客户端通信地址)

       返回新的socket描述符,用来和客户端进行通信

     6 读写数据 read/write

     7 关闭socket 

    注:第一步的socket主要用于等待连接,不参与信息交互。第五步的socket主要用于和客户端之间的通信

    客户端:

     1 调用socke()创建socket,type必须是SOCK_STREAM(保证使用TCP模式)

     2 准备通信地址sockaddr_in

     3 connect()绑定 

     4 通信(read/write)

     5 关闭 close(sockfd)

    TCP---一对多实例:

       服务器端:

           1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<string.h>

          4 #include<sys/socket.h>

          5 #include<netinet/in.h>

          6 #include<arpa/inet.h>

          7 #include<signal.h>

          8 int sockfd;

          9 void fa(){

         10   close(sockfd);

         11   printf("服务器关闭 ");

         12   exit(0);

         13 }

         14 int main(){

         15   signal(SIGINT,fa);//ctrl+c 信号关闭服务器

         16   sockfd = socket(PF_INET,SOCK_STREAM,0);//创建

         17   if(sockfd == -1) perror("socket"),exit(-1);

         18   struct sockaddr_in addr;

         19   addr.sin_family = PF_INET;

         20   addr.sin_port = htons(2222);//端口

         21   addr.sin_addr.s_addr = inet_addr("192.168.13.85");//服务器ip

         22   //解决重启时地址被占用问题

         23   int reuseaddr = 1;

         24  setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,

                       &reuseaddr,sizeof(reuseaddr) );

         25   int res = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));

              //绑定,激活端口

         26   if(res == -1) perror("bind"),exit(-1);

         27   printf("bind ok ");

         28   listen(sockfd,100);//监听

         29   while(1){

         30    struct sockaddr_in from;//存放连接的客户端信息

         31    socklen_t len = sizeof(from);

         32    int fd = accept(sockfd,(struct sockaddr*)&from,&len);

                //等待客户端连接

         33    char* fromip = inet_ntoa(from.sin_addr);

         34    printf("客户端%s连接成功 ",fromip);

         35    pid_t pid = fork();//创建子进程(目前还为学习线程)

         36    if(pid == 0){

         37      while(1){

         38        char buf[100] = {};

         39        read(fd,buf,100);

         40        printf("buf=%s ",buf);

         41        if(strcmp(buf,"byp")==0) break;

         42        write(fd,buf,strlen(buf));

         43      }

         44     close(fd);

         45     exit(0);

         46    }

         47    close(fd);

         48   }

    客户端:

          1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<string.h>

          4 #include<sys/socket.h>

          5 #include<netinet/in.h>

          6 #include<arpa/inet.h>

          7 

          8 int main(){

          9   int sockfd = socket(PF_INET,SOCK_STREAM,0);

         10   if(sockfd == -1) perror("socket"),exit(-1);

         11   struct sockaddr_in addr;

         12   addr.sin_family = PF_INET;

         13   addr.sin_port = htons(2222);//端口

         14   addr.sin_addr.s_addr = inet_addr("192.168.13.85");//服务器ip

         15   int res = connect(sockfd,(struct sockaddr*)&addr,sizeof(addr));//连接

         16   if(res == -1) perror("bind"),exit(-1);

         17   printf("connect ok ");

         18   while(1){

         19     char word[100] ={};

         20     printf("请输入要说的话 ");

         21     scanf("%s",word);

         22     write(sockfd,word,strlen(word));

         23     if(strcmp(word,"bye")==0) break;

         24     char buf[100] = {};

         25     read(sockfd,buf,100);

         26     printf("buf=%s ",buf);

         27   }

         28   close(sockfd);

         29   return 0;

         30 }

      基于TCP协议的服务器和客户端直接的通信除了用read()/write()还可以用 

       recv()/send()

     

    UDP --用户数据报协议

    关于TCP和UDP的区别:

     TCP -- 有连接协议,在通信的全程保持连接

     TCP优点:重发一切错误数据,保证数据的正确和完整,缺点:服务器端压力 

      非常大,资源占用率比较高。

     UDP -- 无连接协议,在发送数据的时候连接一下,不保持任何的连接

     UDP优点:效率高,资源占用少。缺点:不保证数据的完整和正确。

     UDP网络编程的函数 --发送数据和接受数据

       sendto() 和 recvfrom()

    几点注意:

    1 第二步准备通信地址,都是服务器端的通信地址

    2 客户端的通信地址,TCP用accept()函数拿,UDP用recvfrom拿(发送端口)。  

      客户端的端口是自动分配的。

    3 TCP的信息交互函数:read/write/send/recv

      UDP的信息交互函数:read/write/send/recv,但上面的函数不能取得发送方通信地址,因此更多时候,使用sendto/recvfrom

    4 网络信息接收函数 一般会阻塞代码

    5 服务器端都必须使用bind(),bind()函数的作用就是服务器端开发一个端口(把端口和进程绑定起来)。而客户端自动完成的。

    UDP实例:

      服务器端:

          1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<sys/socket.h>

          4 #include<netinet/in.h>

          5 #include<arpa/inet.h>

          6 #include<string.h>

          7 

          8 int main(){

          9 int sockfd =socket(PF_INET,SOCK_DGRAM,0);//创建

         10 if(sockfd == -1) perror("socket"),exit(-1);

         11 struct sockaddr_in addr;

         12 addr.sin_family = PF_INET;

         13 addr.sin_port = htons(2222);//端口

         14 addr.sin_addr.s_addr = INADDR_ANY;//本机IP

         15 int res = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));//放端口

         16 if(res == -1) perror("bind"),exit(-1);

         17 printf("bind ok ");

         18 char buf[100] ={};

         19 struct sockaddr_in from;//用于保存客户端信息,以便回发信息

         20 socklen_t len = sizeof(from);

         21 recvfrom(sockfd,buf,100,0,(struct sockaddr*)&from,&len);//接收信息

         22 //read(sockfd,buf,100);

         23 printf("buf=%s ",buf);

         24 sendto(sockfd,"welcome",7,0,(struct sockaddr*)&from,len);//发送信息

         25 close(sockfd);

         26 return 0;

         27  }

    客户端:

          1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<sys/socket.h>

          4 #include<netinet/in.h>

          5 #include<arpa/inet.h>

          6 #include<string.h>

          7 

          8 int main(){

          9  int sockfd = socket(PF_INET,SOCK_DGRAM,0);

         10  if(sockfd ==  -1) perror("socket"),exit(-1);

         11  struct sockaddr_in addr;

         12  addr.sin_family = PF_INET;

         13  addr.sin_port = htons(2222);

         14  addr.sin_addr.s_addr = INADDR_ANY;

         15 // write(sockfd,"hello",5);没有接收方地址

         16  sendto(sockfd,"hello",5,0,(struct sockaddr*)&addr,sizeof(addr));

         17  char buf[100] ={};

         18 // read(sockfd,buf,100);

         19  struct sockaddr_in from;

         20  socklen_t len =sizeof(from);

         21  recvfrom(sockfd,buf,100,0,(struct sockaddr*)&from,&len);

         22  char* fromip = inet_ntoa(from.sin_addr);

         23  printf("%s发来%d字节信息 ",fromip,len);

         24  printf("%s ",buf);

         25  close(sockfd);

         26  return 0;

         27 }

     

    时间服务器:

      服务端:

         1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<sys/socket.h>

          4 #include<netinet/in.h>

          5 #include<arpa/inet.h>

          6 #include<string.h>

          7 #include<signal.h>

          8 #include<time.h>

          9 

         10 int sockfd;

         11 void fa(int signo){

         12   close(sockfd);

         13   printf("关闭服务器成功! ");

         14   exit(-1);

         15 }

         16 int main(){

         17   signal(SIGINT,fa);

         18   int sockfd =socket(PF_INET,SOCK_DGRAM,0);

         19   if(sockfd == -1) perror("socket"),exit(-1);

         20   struct sockaddr_in addr;

         21   addr.sin_family = PF_INET;

         22   addr.sin_port = htons(2222);//端口

         23   addr.sin_addr.s_addr = INADDR_ANY;//服务器IP

        24   int res = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));//放端口

         25   if(res == -1) perror("bind"),exit(-1);

         26   printf("bind ok ");

         27   while(1){

         28      char buf[100] ={};

         29      struct sockaddr_in from;

         30      socklen_t len = sizeof(from);

         31      recvfrom(sockfd,buf,100,0,(struct sockaddr*)&from,&len);

         32      if(strcmp(buf,"hello")==0){

         33       char ts[100]={};

         34       time_t curtime = time(0);//获得当前时间秒数

         35       struct tm* cur = localtime(&curtime);//将时间秒数转换成tm结构

         36       sprintf(ts,"%4d-%02d-%02d  %02d:%02d:%02d",

                   cur->tm_year+1900,cur->tm_mon+1,cur->tm_mday,

                   cur->tm_hour,cur->tm_min,cur->tm_sec) ;

         38       sendto(sockfd,ts,strlen(ts),0,(struct sockaddr*)&from,len);//发送

         39      }

         40   }

         41 return 0;

         42 }

    客户端:

           1 #include<stdio.h>

          2 #include<stdlib.h>

          3 #include<sys/socket.h>

          4 #include<netinet/in.h>

          5 #include<arpa/inet.h>

          6 #include<string.h>

          7 

          8 int main(){

          9  int sockfd = socket(PF_INET,SOCK_DGRAM,0);

         10  if(sockfd == -1) perror("socket"),exit(-1);

         11  struct sockaddr_in addr;

         12  addr.sin_family = PF_INET;

         13  addr.sin_port = htons(2222);

         14  addr.sin_addr.s_addr = INADDR_ANY;

         15 // write(sockfd,"hello",5);没有接收方地址

         16  sendto(sockfd,"hello",5,0,(struct sockaddr*)&addr,sizeof(addr));

         17  char buf[100] ={};

         18 // read(sockfd,buf,100);

         19  struct sockaddr_in from;

         20  socklen_t len =sizeof(from);

         21  recvfrom(sockfd,buf,100,0,(struct sockaddr*)&from,&len);

         22  char* fromip = inet_ntoa(from.sin_addr);

         23  printf("%s发来%d字节信息 ",fromip,len);

         24  printf("%s ",buf);

         25  close(sockfd);

         26  return 0;

         27 }

     

     

     

     

  • 相关阅读:
    MySQL存储过程和函数
    MySQL数据类型
    MySQL—基础(SQL语句)
    如何将一串字符串按照某个特定的字符分割后倒叙输出,如:www.baidu.com输出为com.baidu.www
    JAVA WEB数据中文编码问题
    如何用一条SQL语句从登录日志表中查询统计出每个人登录的次数
    JAVA WEB tomcat启动关闭问题
    thinkphp知识点
    smarty模板内容
    smarty基础
  • 原文地址:https://www.cnblogs.com/marshhu/p/3258625.html
Copyright © 2011-2022 走看看