网络协议 day38

发布于:2025-07-24 ⋅ 阅读:(23) ⋅ 点赞:(0)

九:网络编程

一:OSI 模型

开放系统互联模型 :分为7层

应用层  
表示层  加密解密  gzip
会话层  网络断开,连接状态,keep-close keep-alive
传输层tcp  udp  协议  文件    视频,音频
网路层ip   NAT
链路层  交换机  数据的格式化  帧 校验
物理层         100Mb/8  Gbits   100MB 同轴电缆 10Gb    2.4G 5G
应用层:为网络用户提供各种服务,例如电子邮件、文件传输等。
表示层:为不同主机间的通信提供统一的数据表示形式。
会话层:负责信息传输的组织和协调,管理进程会话过程。
传输层:管理网络通信两端的数据传输,提供可靠或不可靠的传输服务。
网络层:负责数据传输的路由选择和网际互连。
数据链路层,负责物理相邻(通过网络介质相连)的主机间的数据传输,主要作用包括物理地址寻址、数据帧封装、差错控制等。该层可分为逻辑链路控制子层(LLC)和介质访问控制子层(MAC)。
物理层,负责把主机中的数据转换成电信号,再通过网络介质(双绞线、光纤、无线信道等)来传输。该层描述了通信设备的机械、电气、功能等特性。

二:TCP/IP模型

网际互联模型 :分为4层

tcp/ip协议栈
		应用层   ====》应用程序
		传输层   ====》端口号tcp udp
		网络层   ====》IP 地址
		接口层   ====》网卡 驱动  1GB
2.1TCP/IP协议族
www.taobao.com ---> 192.168.0.19
	www.voa.com vpn  
	dns 域名解析
	DHCP
	应用层: HTTP TFTP FTP SNMP DNS ...
	传输层: TCP传输控制  UDP用户数据报   56k猫
	网络层: IP  ICMP(ping) RIP OSPF IGMP ...
	物理层: ARP  RARP  ...   ip--->mac  
	arp,,,,
	192.160.0.112

三:各种函数

网络接口
1、socket 套接字 ==》BSD socket 》用于网络通信的一组接口函数。socket api application interface
2、ip+port 地址+端口
=》地址用来识别主机
端口用来识别应用程序

	  port分为TCP port / UDP port  范围都是: 1-65535
	  约定1000 以内的端口为系统使用。
3.1socket
include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

功能:程序向内核提出创建一个基于内存的套接字描述符

参数:domain 地址族, PF_INET == AF_INET ==>互联网程序
PF_UNIX == AF_UNIX ==>单机程序
type 套接字类型:
SOCK_STREAM 流式套接字 =TCP
SOCK_DGRAM 用户数据报套接字
=>UDP(用户数据报)//实时性好,但是会丢包
SOCK_RAW 原始套接字 ===》IP
protocol 协议 ==》0 表示自动适应应用层协议。

返回值:成功 返回申请的套接字id, 失败 -1;

3.2bind 客户端连服务器

因为服务器ip地址不经常变,客户端ip地址经常变

int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);

功能:如果该函数在服务器端调用,则表示将参数1相关的文件描述符文件与参数2 指定的接口地址关联, 用于从该接口接受数据。

如果该函数在客户端调用,则表示要将数据从 参数1所在的描述符中取出并从参数2所在的接口设备上发送出去。

注意:如果是客户端,则该函数可以省略,由默认接口发送数据。

参数:sockfd 之前通过socket函数创建的文件描述符,套接字id
my_addr 是物理接口的结构体指针。表示该接口的信息。

struct sockaddr      ////通用地址结构
  {
	  u_short sa_family;  ////地址族
	  char sa_data[14];   ////地址信息
  };
转换成网络地址结构如下:
  struct _sockaddr_in    ///网络地址结构
  {
	  u_short 		  sin_family; ////地址族
	  u_short 		  sin_port;   ///地址端口  设大点,以免找不到空位,也要转:htons(小端转大段)
	  struct in_addr  sin_addr;   ///地址IP   ipconfig命令,到时候转数字且是大端的
	  char 			  sin_zero[8]; ////占位
  };

  struct in_addr
  {
	  in_addr_t s_addr;//到时.两下找到这个
  }

socklen_t addrlen: 参数2 的长度。

​ 返回值:成功 0, 失败 -1;

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

参数:sockfd
如果服务器则是accept的返回值的新fd
如果客户端则是socket的返回值旧fd
buff 用来存储数据的本地内存,一般是数组或者动态内存。
len 要获取的数据长度
flags 获取数据的方式,0 表示阻塞接受。

*src_addr 从哪个接受

*addrlen 长度

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

功能:从msg所在的内存中获取长度为len的数据以flags
方式写入到sockfd对应的套接字中。

参数:sockfd
如果是服务器则是accept的返回值新fd
如果是客户端则是sockfd的返回值旧fd

msg 要发送的消息
len 要发送的消息长度
flags 消息的发送方式。 //默认0.

dest_addr 给谁回

addrlen 长度

返回值:成功 发送的字符长度, 失败 -1;

3.4close() :关闭指定的套接字id;

四:ser/cli

//ser端
// server
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
typedef  struct sockaddr *(SA);	//套接字不是单独给网络用的,可以用别名强转消除警告
int main(int argc, char **argv)
{
  // udp 用户数据报
  int udpfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == udpfd)
  {
    perror("socket");
    return 1;
  }
  //给套接字 设定IP +port
  // man 7 ip
  // ser 是本服务器的地址+port  ,cli 准备存储客户端 的地址+port
  struct sockaddr_in ser, cli;
  bzero(&ser, sizeof(ser));
  bzero(&cli, sizeof(cli));
  ser.sin_family = AF_INET;  // ipv4,同上
  // host to net short
  //小端转大端
  ser.sin_port = htons(50000);//设置大点,以免找不到有空位
  //点分十进制转 大端
  ser.sin_addr.s_addr = inet_addr("192.168.31.33");//对应的ip地址	//ifconfig
  int ret = bind(udpfd, (SA)&ser, sizeof(ser));
  if (-1 == ret)
  {
    perror("bind");
    return 1;
  }
  socklen_t len = sizeof(cli);
  while (1)
  {
    char buf[512] = {0};
    // recvfrom 会阻塞
    recvfrom(udpfd, buf, sizeof(buf), 0, (SA)&cli, &len);
    printf("from cli:%s\n", buf);
    time_t tm;
    time(&tm);
    sprintf(buf, "%s %s", buf, ctime(&tm));
    sendto(udpfd, buf, strlen(buf), 0, (SA)&cli, len);
  }

  close(udpfd);
  // system("pause");
  return 0;
}
//cli端
// client 客户端
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
typedef struct sockaddr *(SA);
int main(int argc, char **argv)
{
  // internet ip v4
  int udpfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == udpfd)
  {
    perror("socket");
    return 1;
  }

  struct sockaddr_in ser;
  bzero(&ser, sizeof(ser));
  ser.sin_family = AF_INET;  // ipv4
  // host to net short
  //小端转大端
  ser.sin_port = htons(50000);
  //点分十进制转 大端
  ser.sin_addr.s_addr = inet_addr("192.168.31.33");

  int i = 30;
  while (i--)
  {
    char buf[512] = "Bhello,";
    sendto(udpfd, buf, strlen(buf), 0, (SA)&ser, sizeof(ser));
    bzero(buf, sizeof(buf));
    //能收到数据的话,也只会是 服务端发送 。

    recvfrom(udpfd, buf, sizeof(buf), 0, NULL, NULL);//客户端负责发送,没必要接受,用NULL
    printf("from ser:%s", buf);
    sleep(1);
  }

  close(udpfd);
  // system("pause");
  return 0;
}

五:双工通信

// server
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
typedef struct sockaddr*(SA);

void* th1(void* arg)
{
  int udpfd = *(int*)arg;
  while (1)
  {
    char buf[512] = {0};
    // recvfrom 会阻塞
    recvfrom(udpfd, buf, sizeof(buf), 0, NULL, NULL);
    if (0 == strcmp(buf, "#quit\n"))
    {
      exit(0);
    }
    printf("from cli:%s", buf);
  }

  return NULL;
}
typedef struct
{
  int udpfd;
  struct sockaddr_in cli;
} TH_ARG;
void* th2(void* arg)
{
  TH_ARG* tmp = (TH_ARG*)arg;
  int udpfd = tmp->udpfd;
  int len = sizeof(tmp->cli);
  while (1)
  {
    char buf[512] = {0};
    printf("to cli:");
    fgets(buf, sizeof(buf), stdin);
    sendto(udpfd, buf, strlen(buf), 0, (SA)&tmp->cli, len);
    if (0 == strcmp(buf, "#quit\n"))
    {
      exit(0);
    }
  }

  return NULL;
}
int main(int argc, char** argv)
{
  // udp 用户数据报
  int udpfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == udpfd)
  {
    perror("socket");
    return 1;
  }
  //给套接字 设定IP +port
  // man 7 ip
  // ser 是本服务器的地址+port  ,cli 准备存储客户端 的地址+port
  struct sockaddr_in ser, cli;
  bzero(&ser, sizeof(ser));
  bzero(&cli, sizeof(cli));
  ser.sin_family = AF_INET;  // ipv4
  // host to net short
  //小端转大端
  ser.sin_port = htons(50000);
  //点分十进制转 大端
  // 本地回环 用于自己接收,自己发送,本地测试网络程序
  ser.sin_addr.s_addr = inet_addr("127.0.0.1");
  int ret = bind(udpfd, (SA)&ser, sizeof(ser));
  if (-1 == ret)
  {
    perror("bind");
    return 1;
  }
  socklen_t len = sizeof(cli);
  pthread_t tid1, tid2;
  char buf[512] = {0};

  //因为udp 无连接,服务器不知道客户端的地址和端口 ,需要客户端自报家门
  recvfrom(udpfd, buf, sizeof(buf), 0, (SA)&cli, &len);
  TH_ARG arg;
  arg.udpfd = udpfd;
  arg.cli = cli;
  pthread_create(&tid1, NULL, th1, &udpfd);
  pthread_create(&tid2, NULL, th2, &arg);

  pthread_join(tid1, NULL);
  pthread_join(tid2, NULL);

  close(udpfd);
  // system("pause");
  return 0;
}
// client 客户端
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
#include <pthread.h>
typedef struct sockaddr *(SA);


void* th1(void* arg)
{
  int udpfd = *(int*)arg;
  while (1)
  {
    char buf[512] = {0};
    // recvfrom 会阻塞
    recvfrom(udpfd, buf, sizeof(buf), 0, NULL, NULL);
    if (0 == strcmp(buf, "#quit\n"))
    {
      exit(0);
    }
    printf("from ser:%s", buf);
  }

  return NULL;
}
typedef struct
{
  int udpfd;
  struct sockaddr_in ser;
} TH_ARG;
void* th2(void* arg)
{
  TH_ARG* tmp = (TH_ARG*)arg;
  int udpfd = tmp->udpfd;
  int len = sizeof(tmp->ser);
  while (1)
  {
    char buf[512] = {0};
    printf("to ser:");
    fgets(buf, sizeof(buf), stdin);
    sendto(udpfd, buf, strlen(buf), 0, (SA)&tmp->ser, len);
    if (0 == strcmp(buf, "#quit\n"))
    {
      exit(0);
    }
  }

  return NULL;
}
int main(int argc, char **argv)
{
  // internet ip v4
  int udpfd = socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == udpfd)
  {
    perror("socket");
    return 1;
  }

  struct sockaddr_in ser;
  bzero(&ser, sizeof(ser));
  ser.sin_family = AF_INET;  // ipv4
  // host to net short
  //小端转大端
  ser.sin_port = htons(50000);
  //点分十进制转 大端
  ser.sin_addr.s_addr = inet_addr("127.0.0.1");

  pthread_t tid1, tid2;
  TH_ARG arg;
  arg.ser = ser;
  arg.udpfd = udpfd;
  char buf[512]="start";
  sendto(udpfd, buf, strlen(buf), 0, (SA)&ser, sizeof(ser));
  pthread_create(&tid1, NULL, th1, &udpfd);
  pthread_create(&tid2, NULL, th2, &arg);

  pthread_join(tid1,NULL);
  pthread_join(tid2,NULL);

  close(udpfd);
  // system("pause");
  return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到