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!\n");

     17  char buf[100]={};

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

     19  printf("读了%d字节k,内容%s\n",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!\n");

     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\n",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("服务器关闭\n");

     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\n");

     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连接成功\n",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\n",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\n");

     18   while(1){

     19     char word[100] ={};

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

     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\n",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\n");

     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\n",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字节信息\n",fromip,len);

     24  printf("%s\n",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("关闭服务器成功!\n");

     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\n");

     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字节信息\n",fromip,len);

     24  printf("%s\n",buf);

     25  close(sockfd);

     26  return 0;

     27 }

 

 

 

 

posted @ 2013-08-14 22:26  我没有领悟  阅读(735)  评论(0编辑  收藏  举报