实现简单的网络套接字编程UDP

一、IP地址

1.作用:在IP协议中,用来标识网络不同主机的地址。
2.IP协议有两个版本,IPv4和IPv6。
IPv4:IP地址有4个字节,共32位。
IPv6:IP地址有16个字节,共128位。
3.通常用“点分十进制”的字符串表示IP地址,用点分割的每一个数表示一个字节,范围是0~255.

在网络传输数据的过程中,IP地址是不变的。IP地址包括源IP地址、目的IP地址。

二、端口号

1.作用:在网络传输过程中,用于标识进程,告诉操作系统,当前的数据要交给哪一个进程来处理。
2.端口号包括:源端口号、目标端口号。
3.端口号是一个2个字节,有16个比特位的整数。
4.一个端口号只能被一个进程占用。
5.只能标记本主机端口号。

因此,我们就知道了,IP地址和端口号可以标识网络上某一台主机的某一进程。我们把IP地址和端口号的组合称为“套接字”。

既然端口号可以标识该主机下的某一进程,那么,端口号与进程ID有什么区别呢?
其实,端口号和进程没有必然的联系。
1个进程可以有多个端口号。
不同的端口可以连接不同的服务器程序,以提供不同的服务。
1个端口号不能被多个进程绑定。

查看端口号
1.在windows下:
查看某个端口号的使用情况,看到底被哪个进程占用,或者需要将其kill掉。
查看命令:netstat -nao | findstr “1234”
taskkill 1234
这里写图片描述
或者在任务管理器下也可以查看:
这里写图片描述
终止:
命令:taskkill [/F] /pid id号
其中,/F是强行终止。
这里写图片描述
2.在Linux下:
netstat -pan | grep 1234
命令:netstat [-a][-e][-n][-o][-p][-b][-r][-s][-v][interval]
参数说明:
-lnp:查看系统当前监听的端口。
-a:显示所有连接和监听端口。
-n:以数字形式显示地址个端口号。
-o:显示每个连接相关的所属进程ID。
-p:在Windows系统中,该选项用于指定默认情况的子集。proto 显示 proto 指定的协议的连接;proto 可以是下列协议之一: TCP、UDP、TCPv6 或 UDPv6。
如果与 -s 选项一起使用以显示按协议统计信息,proto 可以是下列协议之一:IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 或 UDPv6。
-b: 显示包含于创建每个连接或监听端口的可执行组件。在某些情况下已知可执行组件拥有多个独立组件,并且在这些情况下; 包含于创建连接或监听端口的组件序列被显示。这种情况下,可执行组件名在底部的 [] 中,顶部是其调用的组件,等等,直到 TCP/IP 部分。注意此选项
-e:显示以太网统计信息。可与-s选项组合使用。
-s:显示按协议统计信息, 默认地,显示 IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 和 UDPv6 的统计信息。
-r:表示路由表。
-v:与-b选项一起使用时将显示包含于为所有可执行组件创建连接或监听端口的组件。
interval: 重新显示选定统计信息,每次显示之间暂停时间间隔(以秒计)。按 CTRL+C 停止重新显示统计信息。如果省略,netstat 显示当前配置信息(只显示一次)。
这里写图片描述

三、TCP协议

1.是一种有连接可靠的传输协议。
2.面向字节流。

四、UDP协议

1.是一种无连接不可靠的传输协议。
2.面向数据报。

五、网络字节序

在网络传输数据的过程中,发送端通常将发送缓冲区的数据按内存地址从低到高发出,接收端把从网络上接到的字节依次保存在接受缓存区,也是按照内存地址从低到高保存。
TCP/IP协议规定,网络数据采用大端字节序,即低地址高字节。若当前主机是大端机,不需处理直接发送数据,若是小端机,就需要先将数据转换成大端,然后再发送。
小端:低地址存放低字节,高地址存放高字节。
大端:低地址存放高字节,高地址存放低字节。
例:0x1234写入以0x0000开始的内存中
这里写图片描述

实现简单的socket编程

首先,来认识一下socket常见的API
1.创建socket文件描述符
这里写图片描述
参数:
domain:协议族
type:协议类型
protocol:协议编号
返回值:调用成功,返回一个标识该套接字的文件描述符,失败返回-1.

domain可选值及其含义:
这里写图片描述
type设置套接字通信的类型,主要有SOCK_STREAM(流式套接字)和SOCK_GRAM(数据包套接字)等。
type值及其含义:
这里写图片描述
protocol用于限定某个协议的特定类型,即type类型中的某个类型。通常某协议中只有一种特定类型,protocol参数仅能设置为0,对于有的协议有多种特定类型,就需要设置这个参数来选择特定类型。

* 类型为SOCK_STREAM的套接字表示一个双向的字节流,与管道类似。流式的套接字在进行数据收发之前必须已经连接,连接使用connect()函数进行。一旦连接,可以使用read()或者write()函数进行数据的传输。流式通信方式保证数据不会丢失或者重复接收,当数据在一段时间内任然没有接受完毕,可以将这个连接人为已经死掉。

* SOCK_DGRAM和SOCK_RAW 这个两种套接字可以使用函数sendto()来发送数据,使用recvfrom()函数接受数据,recvfrom()接受来自制定IP地址的发送方的数据。

* SOCK_PACKET是一种专用的数据包,它直接从设备驱动接受数据。

2.绑定端口号
这里写图片描述
参数:
sockfd:用socket函数创建的文件描述符。
addr:指向一个结构为sockaddr参数的指针,sockaddr包含了地址、端口和IP地址等信息。

数据类型:

struct sockaddr
    {
       unsigned short  sa_family;   //地址族, 一般为AF_INET
       char                  sa_data[14];   //14字节的协议地址
    }

    struct sockaddr_in
    {
       short int                     sin_family;   //地址族
       unsigned short int      sin_port;      //端口号
       struct in_addr             in_addr;      //ip地址
       unsigned char             sin_zero[8];  //填充
    }

addrlen:是addr结构的长度。
返回值:返回值为0表示绑定成功,-1表示绑定失败,errno的错误值如下:
这里写图片描述
3.监听socket
这里写图片描述
参数:
backlog:在TCP层接收链接的缓冲池的最大个数

4.接收请求
这里写图片描述
5.建立链接
这里写图片描述

client.c(服务器)

#include <stdlib.h>                                                         
  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <string.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>


  int main(int argc, char* argv[])
  {
      int sock = socket(AF_INET, SOCK_DGRAM, 0);
      if(sock < 0){
          perror("socket");
          return 2;
      }
      struct sockaddr_in server;
      server.sin_family = AF_INET;
      server.sin_port = htons(atoi(argv[2]));
      server.sin_addr.s_addr = inet_addr(argv[1]);

      char buf[1024];
      struct sockaddr_in peer;
     while(1){
          socklen_t len = sizeof(peer);
          printf("Please enter:");
          fflush(stdout);
          ssize_t s = read(0, buf, sizeof(buf)-1);
          if(s > 0){
              buf[s-1]=0;
              sendto(sock, buf, strlen(buf),0,\
                      (struct sockaddr*)&server, sizeof(server));
              ssize_t _s = recvfrom(sock, buf, sizeof(buf)-1, 0,\
                      (struct sockaddr*)&peer, &len);
              if(_s > 0){
                  buf[_s] = 0;
                  printf("server:%s\n", buf);
              }
          }
      }
      return 0;
  }

server.c(服务器端)

 #include <stdio.h>                                                          
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <string.h>
  #include <stdlib.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>

  int main(int argc, char* argv[])
  {
      if(argc!=3)
      {
          printf("please enter ip and port\n");
          exit(1);
      }
      int sock = socket(AF_INET, SOCK_DGRAM, 0);//TCP连接
      if(sock < 0){
          perror("socket");
          return 2;
      }

      struct sockaddr_in local;
      local.sin_family = AF_INET;
        local.sin_port = htons(atoi(argv[2]));
      local.sin_addr.s_addr = inet_addr(argv[1]);

      if(bind(sock, (struct sockaddr*)&local, sizeof(local))<0){
          perror("bind");
          return 3;
      }

      char buf[1024];
      struct sockaddr_in client;
      while(1){
          socklen_t len = sizeof(client);
          ssize_t s = recvfrom(sock, buf, sizeof(buf)-1, 0, \
                  (struct sockaddr*)&client, &len);
          if(s > 0){
              buf[s]= 0;
              printf("[%s:%d]:%s\n",inet_ntoa(client.sin_addr),\
                      ntohs(client.sin_port), buf);
              sendto(sock, buf, strlen(buf), 0, \
                      (struct sockaddr*)&client, sizeof(client));
          }
      }
  }              

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值