Linux中网络基础和SOCKET-学习笔记(八)

本文深入解析网络通信的基本概念,包括协议、网络设计模式、网络基础如OSI七层模型与TCP/IP模型,以及数据包封装过程。重点介绍了单向与双向通信的区别,详细阐述了socket编程的核心技术,包括网络字节序转换函数、IP地址和端口号的概念,并通过具体程序实例展示了服务器与客户端的通信流程。
摘要由CSDN通过智能技术生成
1、协议概念
指定规则:先传输文件名,再传输文件大小,最后传输数据。如ftp协议,传输固定数据,遵守一定的格式。
标准协议:如http、tcp/ip,arp等。
2、网络设计模式
c/s架构:client/server   
         特点:要求开发客户端和服务端,协议采用自定义方式;必须先下载客户端,数据提前缓存好。
         不足:安全性不高;开发工作量大
b/s架构:web/server
         特点:不需要安装软件;工作量小,客户端基本采用浏览器方式
         不足:要求遵守fttp协议,动态加载数据
3、网络基础
(1)OSI七层模型和TCP/IP模型区别在于划分标准不一致,实质一样。
1)物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型等,主要作用是传输比特流。
2)数据链路层:定义了如何让格式化数据以进行传输,主要作用为提供错误检测和纠正。
3)网络层:为两个主机系统之间提供连接和路径选择,即用于传输。
4)传输层:定义了一些传输数据的协议和端口号,如:TCP和UDP,TCP为点对点传输,UDP为数据传送到某个地方,再从该处进行传送,传送到某个地方的过程中容易出现数据丢失,及不定时传输的情况,故为不可靠传输。
(2)数据包封装-简单理解即为:
发送数据时,应用层先对数据进行处理,然后再到传输层,之后为网络层,最后链路层对数据进行前后处理,而在接收数据时端则是数据链路层先进行数据解包,之后网络层对数据包处理,接着传输层进行处理,再到应用层处理,最后将应用层数据交给应用程序处理。
(3)单向通信和双向通信
单向通信:使用用2个文件描述符即可;
双向通信:需在客户端设置发送端文件描述符和接收端文件描述符,服务端亦是如此。客户端发送端文件描述符对应服务端接收端文件描述符;客户端接收端文件描述符对应服务端发送端文件描述符。
4、网络通信过程

5、socket编程
(1)网络字节序转换函数
uint32_t htonl(uint32_t hostlong);//将ip地址转换为int类型,再转换为网络字节序
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h表示host,n表示network,l表示32位长整数,s表示16位短整数。
注:若主机为小端字节序,函数将参数做相应的大小端转换然后返回,若主机为大端字节序,函数不做转换,将参数原封不动地返回。
(2)IP地址和端口号
ip地址:对应主机,即在网络环境中,唯一识别一台主机。
端口号:端口相当于主机中的一个进程,即在主机中唯一识别一个进程。
(3)程序
//程序1 tcpserver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//定义服务器端口号
#define SERVER_PORT 8000

int main(void)
{
    //定义结构体类型变量 服务端地址,客户端地址
    struct sockaddr_in serveraddr,clientaddr;
    //定义服务器端监听套接字,通信套接字
    int sockfd,confd;
    int addrlen;//客户端地址结构体变量所占空间大小
    //自定义数组,变量
    char ipstr[128];//存储客户端IP地址
    char buf[1024];//储存从通信套接字读取到的数据
    int len;//从通信套接字读取的数据字节数
    int i=0;//普通循环变量=0
    
    //1、创建socket套接字 参数:ipv4,TCP方式,默认协议
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    //2、bind将监听套接字与服务器ip地址和端口绑定
    bzero(&serveraddr,sizeof(serveraddr));//服务器端地址结构体变量清零
    serveraddr.sin_family=AF_INET;//地址协议族为ipv4
    serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);//连接任意ip,字节序转换主机到网络
    serveraddr.sin_port=htons(SERVER_PORT);//端口为8000
    //参数 监听套接字,客户端地址结构体变量,转换后的客户端地址结构体变量所占空间大小
    bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//bind
    //3、监听客户端,参数:监听套接字,监听上限数
    listen(sockfd,128);
    
    while(1)
    {
        addrlen=sizeof(clientaddr);//计算客户端地址结构体变量所占空间大小
        //4、阻塞监听客户端连接请求 参数:监听套接字,客户端地址结构体变量,客户端地址结构体变量所占空间大小
        confd=accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen);
        printf("client ip %s \t port %d \n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),ntohs(clientaddr.sin_port));
        //5、读取通信套接字中数据,处理客户端请求 参数:通信套接字,数据缓存数组,需读取数据字节大小
        len=read(confd,buf,sizeof(buf));
        while(i<len)
        {
            buf[i]=toupper(buf[i]);//将读取到的字母变为大写
            i++;
        }
        //数据写回到客户端 参数:通信套接字,存储读取到的数据数组,需写入数据字节大小
        write(confd,buf,len);
        close(confd);//关闭通信套接字
    }
    close(sockfd);//关闭监听套接字
    return 0;
}
//程序2 tcpclient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

//定义服务器端口
#define SERVER_PORT 8000

int main(int argc ,char *argv[])
{
    //定义结构体类型变量 服务端地址
    struct sockaddr_in serveraddr;
    //定义客户器端通信套接字
    int confd;
    int addrlen;//服务端地址结构体变量所占空间大小
    //自定义数组
    char ipstr[]="192.168.60.128";//存储服务器IP地址
    int len;//从通信套接字读取的数据字节数
    char buf[1024];//储存从通信套接字读写的数据
    
    //1、创建通信套接字  参数:ipv4,TCP方式,默认协议
    confd=socket(AF_INET,SOCK_STREAM,0);
    
    //2、bind将监听套接字与服务器ip地址和端口绑定
    bzero(&serveraddr,sizeof(serveraddr));//服务器端地址结构体变量清零
    serveraddr.sin_family=AF_INET;//地址协议族为ipv4
    serveraddr.sin_port = htons(SERVER_PORT); //端口为8000
    //IP地址字符串转为网络字节序
    inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);
    
    //3、连接服务器 参数:通信套接字,服务器端地址结构体变量,服务器端地址结构体变量所占空间大小
    connect(confd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    //4、写数据到服务器
    write(confd,argv[1],strlen(argv[1]));
    //5、从服务器读取数据
    len=read(confd,buf,sizeof(buf));
    //显示到终端
    write(STDOUT_FILENO,buf,len);
    printf("\n");
    return 0;
}
程序1和程序2结合使用,程序执行效果:

 

//程序3 tcpserver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//定义服务器端口号
#define  SERVER_PORT 6666

int main(int argc,char *argv[])
{
    //定义结构体类型变量 服务端地址,客户端地址
    struct sockaddr_in serveraddr,clientaddr;
    //定义服务器端监听套接字,通信套接字
    int sockfd,confd;
    int addrlen;//客户端地址结构体变量所占空间大小
    //自定义数组
    char ipstr[128];//存储客户端IP地址
    char buf[1024];//储存从通信套接字读取到的数据
    int len;//从通信套接字读取的数据字节数
    
    //1、创建socket套接字 参数:ipv4,TCP方式,默认协议
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    //2、bind将监听套接字与服务器ip地址和端口绑定
    bzero(&serveraddr,sizeof(struct sockaddr_in));//服务器端地址结构体变量清零
    serveraddr.sin_family=AF_INET;//地址协议族为ipv4
    serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);//连接任意ip,字节序转换主机到网络
    serveraddr.sin_port=htons(SERVER_PORT);//端口为6666
    //参数 监听套接字,客户端地址结构体变量,转换后的客户端地址结构体变量所占空间大小
    bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr));
    //3、监听客户端,参数:监听套接字,监听上限数
    listen(sockfd,5);
    
    while(1)
    {
        addrlen=sizeof(clientaddr);//计算客户端地址结构体变量所占空间大小 //法1
        //addrlen=sizeof(struct sockaddr_in);//法2
        //4、阻塞监听客户端连接请求 参数:监听套接字,客户端地址结构体变量,客户端地址结构体变量所占空间大小
        confd=accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen);
        //显示请求的客户端IP地址
        fprintf(stderr,"server get connect from %s\n",inet_ntoa(clientaddr.sin_addr));
        //5、读取通信套接字中数据,处理客户端请求 参数:通信套接字,数据缓存数组,需读取数据字节大小
        len=read(confd,buf,sizeof(buf));
        buf[len]='\0';
        printf("server received :%s\n",buf);
        //关闭通信套接字
        close(confd);
    }
    close(sockfd);//关闭监听套接字
    return 0;
}
//程序4 tcpclient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

//定义服务器端口
#define  SERVER_PORT 6666

int main(int argc,char *argv[])
{
    ///定义结构体类型变量 服务端地址,客户端地址
    struct sockaddr_in serveraddr,clientaddr;
    //定义客户端套接字
    int sockfd;
    int addrlen;//客户端地址结构体变量所占空间大小
    //自定义数组
    int len;//从通信套接字读取的数据字节数
    char buf[1024];//储存从通信套接字读取到的数据
    
    //1、创建socket套接字 参数:ipv4,TCP方式,默认协议
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    //2、bind将客户端套接字与客户端ip地址和端口绑定
    bzero(&clientaddr,sizeof(struct sockaddr_in));//服务器端地址结构体变量清零
    clientaddr.sin_family=AF_INET;//地址协议族为ipv4
    clientaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    clientaddr.sin_port=htons(SERVER_PORT);//端口为6666
    //参数 客户端套接字,客户端地址结构体变量,转换后的客户端地址结构体变量所占空间大小
    bind(sockfd,(struct sockaddr *)&clientaddr,sizeof(struct sockaddr));
    
    //客户程序填充服务端的资料
    bzero(&serveraddr,sizeof(serveraddr));//服务器端地址结构体变量清零
    serveraddr.sin_family=AF_INET;//地址协议族为ipv4
    serveraddr.sin_port=htons(SERVER_PORT);//端口为6666
    serveraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    //3、连接服务器 参数:通信套接字,服务器端地址结构体变量,服务器端地址结构体变量所占空间大小
    connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
    
    //4、写数据到服务器
    printf("please input char:\n");
    fgets(buf,1024,stdin);//从键盘输入数据
    write(sockfd,buf,strlen(buf));//写数据到服务端
    //关闭客户端套接字
    close(sockfd);
    return 0;
}
程序3和程序4结合使用,程序执行效果:

 

注:
(1)服务器端程序中若监听放到while外面,只监听一次。
(2)客户端使用固定端口号时,可调用bind()。当客户端不需要固定的端口号,可以不调用bind(),客户端的端口号由内核自动分配。
(3)服务器端需要调用bind(),若服务器不调用bind(),内核会自动给服务器分配监听端口,则每次启动服务器时端口号都不一样,将导致客户端连接服务器出错。

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值