UC成长之路13

Posted 达少Rising

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UC成长之路13相关的知识,希望对你有一定的参考价值。

回顾:
UC成长之路1
UC成长之路2
UC成长之路3
UC成长之路4
UC成长之路5
UC成长之路6
UC成长之路7
UC成长之路8
UC成长之路9
UC成长之路10
UC成长之路11
UC成长之路12

一、基于TCP网络编程模型和实现

  • 传输层有两种:TCP和UDP

  • TCP面向连接的、可靠的、安全的但效率低

  • UDP面向包的、不可靠的但效率高

  • 建立TCP连接与断开:建立连接过程称为著名的三次握手,断开四次挥手
    在这里插入图片描述

  • 服务器端编程模型

	//1、创建一个套接字端点,返回一个文件描述符。lfd
	socket(2)
	//2、将lfd和服务器的ip地址和端口号绑定
	bind(2)
	//3、将lfd设置成被动连接模式,监听客户端连接的到来。有客户端连接的到来,放入未决连接队列中
	listen(2)
	//4、从未决连接队列中取出一个客户端连接,返回和客户端连接的文件描述符。使用这个文件描述符和客户端通讯。未决连接队列中没有数据,阻塞等待客户端的连接到来
	accept(2)
	while(1){
		//5从客户端获取数据
		read()
		//6、处理获取到的数据
		//7、将处理结果回送给客户端
		write()
		//8、关闭本次连接
		close()
	}
  • 客户端的编程模型
	//1、创建一个通讯端点,返回一个文件描述符
	socket(2)
	//2、使用这个文件描述符向服务器发起连接
	connect(2)
	//3、向服务器发送消息
	write()
	//4、等待服务器的相应消息
	read()
	//5、处理服务器的相应消息
	//6、关闭和服务器的连接,借宿通讯
	close()
  • socket(2)
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//功能:创建一个通讯端点,返回一个文件描述符
//参数
//domain:
//1)AF_INET             IPv4 Internet protocols          ip(7)
//2)AF_INET6            IPv6 Internet protocols          ipv6(7)
//...
//type
//1)SOCK_STREAM:TCP
//2)SOCK_DGRAM:UDP
//...
//protocol:0
//返回值:成功返回一个用于新的socket的文件描述符;错误返回-1,errno被设置
  • bind(2)
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//功能:给socket绑定一个名字
//参数:
//sockfd:指定了具体的socket(像手机的SIM卡插槽)
//addr:指定了服务器的地址和端口号(像SIM卡)
//addrlen:制定了addr的有效空间大小
//返回值:成功返回0;错误返回-1,error被设置

struct sockaddr {
      sa_family_t sa_family;
      char  sa_data[14];
};
  • listenl(2)
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
//功能:在指定socket上建立连接
//参数:
//sockfd:socket(2)的返回值
//backlog:指定了未决连接队列的最大长度
//返回值:成功返回0;错误返回-1,error被设置
  • accept(2)
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//功能:在指定的socket上接收连接
//参数:
//sockfd:指定了socket(2)。socket(2)的返回值
//addr:如果addr是NULL,addrlen也要设置为空;
//addr指定的地址空间里填充客户端的地址家族的内容
//addrlen:值-结果参数,指定了addr的有效空间大小
//返回值:错误返回-1,errno被设置;成功返回一个非负整数,一个文件描述符,
//和客户端的连接描述符,使用这个连接描述符和客户端通讯。
  • connect(2)
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//功能:在一个socket上发起一个连接,将socket连接到addr指向的地址上
//参数:
//sockfd:指定了socket
//addr:指定了地址
//addrlen:指定了addr空间的大小
//返回值:成功返回0;错误返回-1,errno被设置
  • IPV4家族、IPV6家族
  • 通用家族 struct sockaddr
struct sockaddr {
      sa_family_t sa_family;
      char  sa_data[14];
};
  • IPV4家族地址,使用man 7 ip查看
struct sockaddr_in {
       sa_family_t  sin_family; /* address family: AF_INET */
       in_port_t    sin_port;   /* port in network byte order */
       struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
        uint32_t   s_addr;     /* address in network byte order */
};
  • 配置端口号,配置ip地址
  • 网络字节序与主机字节序相互转换, htonl(3)
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
//h:host
//n:net
//s:short
//l:long
//to
  • ip地址的转化:字符串与无符号的长整型转换,inet_pton(3), inet_ntop(3)
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
//功能:text-->binary
//参数:
//af:AF_INET or AF_INET6
//src:字符串格式的ip,待转换的
//dst:存放转换后的结果
//返回值:成功返回1;af无效返回-1,errno被设置;src无效返回0

#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,
              		char *dst, socklen_t size);
//功能:binary--->text
//参数:
//af:AF_INET or AF_INET6
//src:ip地址(binary)
//dst:转换后的ip地址(text)存放到这个地址指定的空间里
//size:指定dst缓冲区可用的字节数
//返回值:错误返回NULL,errno被设置;成功返回dst指向的地址,转换后的结果存放这个空间里       		

eg:编写基于TCP的服务器端和客户端程序

  • 服务器端负责将客户端发送过来的字符串转换为大写,server.c
  • 客户端负责向服务器发送字符串,然后将服务器转换后的字符串输出到显示器,client.c
    • server.c
#include <stdio.h>
#include <sys/types.h>     
#include <sys/socket.h>
#include <ctype.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void)
{
    struct sockaddr_in serv;
    int cfd;//connect fd
    char buf[128];
    //創建1個socket,返回該socket的文件描述符ldf
    int lfd = socket(AF_INET, SOCK_STREAM, 0); 
    if(lfd==-1){
        perror("socket");
        return -1; 
    }   
    //初始化serv的成員
    serv.sin_family=AF_INET;
    serv.sin_port=htons(1024);
    //INADDR_ANY代表本機所有的ip地址
    serv.sin_addr.s_addr=htonl(INADDR_ANY);
    //將lfd綁定到local的ip地址和端口
    int b = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
    if(b==-1){
        perror("bind");
        return -1; 
    }   
    //將lfd設置成被動鏈接模式,監聽客戶端連接的到來。如果有客戶端的連接到來,將該連接放入未決連接隊列中
    listen(lfd, 5); 
    while(1){
        //從未決連接隊列中取出第一個連接進行處理,如果沒有未決連接,阻塞等待,有,返回一個文件描述符
        cfd = accept(lfd, NULL, NULL);
        if(cfd==-1){
            perror("accept");
            return -1; 
        }
        //這時候,三次握手已經完成,數據的處理
        //讀取客戶端的請求
        int r = read(cfd, buf, 128);
        int i;
        //處理客戶端的請求
        for(i=0; i<r; i++){
            buf[i]=toupper(buf[i]);//將字符轉換爲大寫
        }
        //響應客戶端,將處理信息響應給客戶端
        write(cfd, buf, r); 
        //關閉和客戶端的連接,結束這個連接
        close(cfd);
    }   
    return 0;
}
  • client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void)
{
    struct sockaddr_in serv;//服務器的ip地址和端口號
    char *msg = "this is a test...\\n";
    char buf[128];
    //創建一個socket,返回該socket的文件描述副符
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd==-1){
        perror("socket");
        return -1;
    }
    //初始化服務器的ip地址和端口號
    serv.sin_family=AF_INET;//IPV4
    serv.sin_port=htons(1024);
    //服務器的ip地址,127.0.0.1,text-->binary
    inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr);
    //使用lfd向服務器發起連接,如果這個函數執行成功,三次握手成功
    int conn=connect(lfd, (struct sockaddr*)&serv, sizeof(serv));
    if(conn){
        perror("connect");
        return -1;
    }
    //向服務器發送字符串
    write(lfd, msg, strlen(msg));
    //等待服務器的響應信息,如果沒有響應阻塞等待
    int r=read(lfd, buf, 128);
    //將獲取到的響應信息輸出到顯示器
    write(1, buf, r);
    //關閉和服務器的連接,結束通訊
    close(lfd);
    return 0;
}

在这里插入图片描述

  • 改进eg
    server.c
#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <ctype.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(void)
{
    struct sockaddr_in serv, clie;
    int cfd;//connect fd
    char buf[128];
    //創建1個socket,返回該socket的文件描述符ldf
    int lfd = socket(AF_INET, SOCK_STREAM, 0); 
    if(lfd==-1){
        perror("socket");
        return -1; 
    }   
    //初始化serv的成員
    serv.sin_family=AF_INET;
    serv.sin_port=htons(1024);
    //INADDR_ANY代表本機所有的ip地址
    serv.sin_addr.s_addr=htonl(INADDR_ANY);
    //將lfd綁定到local的ip地址和端口
    int b = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
    if(b==-1){
        perror("bind");
        return -1; 
    }   
    //將lfd設置成被動鏈接模式,監聽客戶端連接的到來。如果有客戶端的連接到來,將該連接放入未決連接隊列中
    listen(lfd, 5); 
    while(1){
        socklen_t cli_len = sizeof(clie);
        //從未決連接隊列中取出第一個連接進行處理,如果沒有未決連接,阻塞等待,有,返回一個文件描述符
        cfd = accept(lfd, (struct sockaddr *)&clie, &cli_len);
        if(cfd==-1){
            perror("accept");
            return -1; 
        }
        char IP[64];
        //binary-->text
        printf("%s\\n", inet_ntop(AF_INET, &clie.sin_addr, IP, 64));
        //這時候,三次握手已經完成,數據的處理
        //讀取客戶端的請求
        int r = read(cfd, buf, 128);
        int i;
        //處理客戶端的請求
        for(i=0; i<r; i++){
            buf[i]=toupper(buf[i]);//將字符轉換爲大寫
        }
        //響應客戶端,將處理信息響應給客戶端
        write(cfd, buf, r); 
        //關閉和客戶端的連接,結束這個連接
        close(cfd);
    }   
    return 0;
}

client.c

        perror("connect");
        return -1; 
    }   
    //向服務器發送字符串
    write(lfd, msg, strlen(msg));
    //等待服務器的響應信息,如果沒有響應阻塞等待
    int r=read(lfd, buf, 128);
    //將獲取到的響應信息輸出到顯示器
    write(1, buf, r); 
    //關閉和服務器的連接,結束通訊
    close(lfd);
    return 0;
}

在这里插入图片描述

  • 封装eg
    t_net.h
#ifndef T_NET_H_
#define T_NET_H_

/*include file*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*類型的聲明*/
typedef struct sockaddr_in SA4;
typedef struct sockaddr SA; 

/*函數的聲明,創建socket,將返回的描述符綁定到本地地址*/
int socket_b(int port);
int trans(int fd);//客戶端的業務處理

#endif

t_net.c

#include <stdio.h>
#include "t_net.h"

int socket_b(int port)
{
    SA4 serv;
    //創建1個socket,返回該socket的文件描述符ldf
    int lfd = socket(AF_INET, SOCK_STREAM, 0); 
    if(lfd==-1){
        perror("socket");
        return -1; 
    }   
    //初始化serv的成員
    serv.sin_family=AF_INET;
    serv.sin_port=htons(port);
    //INADDR_ANY代表本機所有的ip地址
    serv.sin_addr.s_addr=htonl(INADDR_ANY);
    //將lfd綁定到local的ip地址和端口
    int b = bind(lfd, (SA *)&serv, sizeof(serv));
    if(b==-1){
        perror("bind");
        return -1; 
    }   
    return lfd;
}

//業務的處理
int trans(int fd) 
{
    char buf[128];
    //讀取客戶端的請求
    int r=read(fd,buf,128);
    int i;
    //處理客戶端的請求
    for(i=0;i<r;i++){
        buf[i]=toupper(buf[i]);//將字符轉換爲大寫
    }   
    write(fd, buf, r); 
    return 0;
}

serverp.c

#include <stdio.h>
#include <unistd.h>
#include "t_net.h"

int main(void)
{
    SA4 clie;
    int cfd;//connect fd
    int lfd=socket_b(1024);
    if(lfd==-1) return -1; 
    //將lfd設置成被動鏈接模式,監聽客戶端連接的到來。如果有客戶端的連接到來,將該連接放入未決連接隊列中
    listen(lfd, 5); 
    while(1){
        socklen_t cli_len = sizeof(clie);
        //從未決連接隊列中取出第一個連接進行處理,如果沒有未決連接,阻塞等待,有,返回一個文件描述符
        cfd = accept(lfd, (SA *)&clie, &cli_len);
        if(cfd==-1){
            perror("accept");
            return -1; 
        }
        char IP[64];
        //binary-->text
        printf("%s\\n", inet_ntop(AF_INET, &clie.sin_addr, IP, 64));
        //這時候,三次握手已經完成,數據的處理
        trans(cfd);//業務的處理
        //關閉和客戶端的連接,結束這個連接
        close(cfd);
    }   
    return 0;
}

client.c

    inet_pton(AF_INET, argv[1], &serv.sin_addrSpark成长之路(13)-DataSet与DataFrame

开发成长之路(13)-- Linux网络服务端编程(通识篇)

开发成长之路(13)-- Linux网络服务端编程(通识篇)

python成长之路第三篇_正则表达式

python成长之路第三篇_正则表达式

如何成为一名架构师,架构师成长之路(转)