part9 网络编程

linux网络编程框架

网络是分层的

OSI 7层模型
网络通信是极其复杂的一种通信

TCP/IP协议引入

TCP/IP是用的最多的网络协议实现
TCP/IP分为四层对应OSI的七层
我们编程时最关注应用层,了解传输出,网际互联层和网络接入层不用管

BS和CS

CS架构介绍:(client server    客户端服务器架构)
BS架构介绍:(broswer server      浏览器服务器架构)

TCP协议的学习

理解TCP的重点

TCP协议工作在传输层,对上服务socket接口。对下调用IP层
TCP协议面向连接,通信前必须先三次握手建立连接关系后才能开始通信
TCP协议提供可靠传输,不怕丢包、乱序等

TCP如何保证可靠传输

TCP在传输有效信息前要求通信双方必须先握手,建立连接才能通信
TC在接受方收到数据包后会ack给发送方,若发送方未收到ack会丢包重传
TCP的有效数据内容会附带校验,以防止内容在传递过程中损坏
TCP会根据网络带宽来自动调节适配速率(滑动窗口技术)
发送方会给个 分割报文编号,接收方会校验编号,一旦顺序错误就会重传 

TCP的三次握手

建立连接需要三次握手
建立连接的条件:服务器listen时客户端主动发起connect

TCP的四次握手

关闭连接需要四次握手
服务器或者客户端都可以主动发起关闭

基于TCP通信的服务模式

具有公网IP地址的服务器(或者使用动态IP地址映射技术)
服务器端soket、bind、listen后处于监听状态
客户端socket后,直接connect去发起连接
服务器收到同意客户端接入后会建立TCP连接,然后双方开始收发数据,收发时是双向的,双方均可发起,双方均可关闭连接

常见的使用了TCP协议的网络应用

http、ftp
QQ服务器
mail服务器

socket编程接口介绍

建立连接

socket
socket函数类似于open,用来打开一个网络连接,如果成功则返回一个网络文件描述符(int类型),之后操作网络连接都通过这个文件描述符
bind(绑定函数)
listen
connect

发送和接收

send和write
recv和read

辅助性函数

inet_aton、inet_addr、inet_ntoa
inet_ntop、inet_pton

表示IP地址相关数据结构

1、都定义在netinet/in.h
2、struct sockaddr,这个结构体是网络编程接口中用来表示一个IP地址的,注意这个地址是不区分IPv4和IPv6(兼容)
3、typedef uint32_t inaddr_t;网络内部用来表示IP地址的类型
4、struct in_addr(不常用)
{
        in_addr_t s_addr;
}
5、struct sockaddr 是linux的网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都需这个结构体,此结构体兼容IPv4和IPv6,。在实际编程中此结构体会被一个struct sockaddr_in或者 struct sockaddr_in6 所替代(填充)
6、struct sockaddr_in IPv4
7、struct sockaddr_in6 IPv6
3、in_addr_t、struct in_addr、struct sockaddr_in

IP地址格式转换函数实践

inet_addr、inet_ntoa、inet_aton
inet_addr:不兼容IPv6

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

#define IPADDR "192.168.1.102"

///0x66   01   a8    c0
///102    1    168   192

int main()
{
    in_addr_t addr = 0;
    addr = inet_addr(IPADDR);
    printf("addr = 0x%x.\n",addr);
    printf("addr = 0x%x.\n",addr);
    return 0;
}


inet_pton、inet_ntop:兼容IPV6使用方式查看man手册

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

#define IPADDR "192.168.1.102"

///0x66   01   a8    c0
///102    1    168   192

int main()
{
    int ret = 0;
    struct in_addr addr = {0};;
    ret = inet_pton (AF_INET, IPADDR, &addr);
    if(ret != 1)
    {
        printf("inet_pton error.\n");
        return -1;
    }
    printf("addr = 0x%x.\n",addr.s_addr);
    return 0;
}

网络字节序:大端模式

socket实践编程

服务器编写:

socket
bind
listen

htonl(host to net long)将主机字节序转成网络字节序四个字节
htons(host to net short)将主机字节序转成网络字节序两个字节

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

#define SERADDR "192.168.190.174" ///ifconfig看到的
#define MYPORT 8910
#define BACKLOG 100
int main(void)
{
     int sockfd = -1,ret = -1;
     struct sockaddr_in seraddr = {0};
     struct sockaddr_in cliaddr = {0};
     socklen_t len = 0;
    ///第一步:先socket打开文件描述符
    ///AF_INET表示IPv4,SOCK——STREAM表示TCP协议
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket: .\n");
        return -1;
    }
    printf("soketfd = %d.\n", sockfd);
    ///第二步:bind绑定socket和当前电脑的ip地址和端口号
    seraddr.sin_family = AF_INET;///到底是IPv4还是IPv6,设置地址族
    seraddr.sin_port = htons(MYPORT);///端口号
    seraddr.sin_addr.s_addr = inet_addr(SERADDR);///IP地址
    ret = bind(sockfd, (const struct sockaddr*)&seraddr, sizeof(seraddr));
    if(ret < 0)
    {
        perror("bind: ");
        return -1;
    }
    printf("bind success.\n");
    ///第三步:listen监听端口
    ret = listen(sockfd, BACKLOG);
    if(ret < 0)
    {
        perror("listen");
        return -1;
    }
    ///第四步:accept阻塞等待客户端
    ret = accept(sockfd, (struct sockaddr *)&cliaddr, &len);

    return 0;
}

客户端编写:

socket
connect

概念:端口号,实质是一个数字编号,用来在我们一台主机中(主机的操作系统中)唯一的标识一个能上网的进程。端口号和IP地址一起会被打包到当前进程发出或者接收到的每个数据包中。 每一个数据包将来在网络上传递的时候,内部都包含了发送方和接收方的信息(就是IP地址和端口号),所以IP地址和端口号这两个往往是打包在一起不分家的。

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


#define SERADDR "192.168.190.174" ///ifconfig看到的
#define MYPORT 8910
char sendbuf[100];

int main()
{
    int sockfd = -1,ret = -1;
     struct sockaddr_in seraddr = {0};
     struct sockaddr_in cliaddr = {0};

    ///第一步:先socket打开文件描述符
    ///AF_INET表示IPv4,SOCK——STREAM表示TCP协议
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket: .\n");
        return -1;
    }
    printf("soketfd = %d.\n", sockfd);
    ///第二步:connect链接服务器
    seraddr.sin_family = AF_INET;///到底是IPv4还是IPv6,设置地址族
    seraddr.sin_port = htons(MYPORT);///端口号
    seraddr.sin_addr.s_addr = inet_addr(SERADDR);///IP地址
    ret = connect(sockfd, (const struct sockaddr*)&seraddr, sizeof(seraddr));
    if(ret < 0)
    {
        perror("bind: ");
        return -1;
    }
    ret = recv(sockfd, sendbuf, sizeof(sendbuf), 0);
    printf("成功接收了%d个字节\n",ret);
    printf("client发送过来的内容是: %s\n", sendbuf);
    return 0;
}

自定义应用层协议

第一步:规定发送和接收方法
规定连接建立后由客户端主动向服务器发出1个请求数据包,让服务器收到数据包后回复客户端一个回应数据包,这就是一个通信回合
整个连接的通信就是由N多个回合组成的。

第二步:定义数据包格式

客户端代码

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


#define SERADDR "192.168.190.174" ///ifconfig看到的
#define MYPORT 8910
#define CMD_REGISTER 1001 ///注册学生信息
#define CMD_CHECK 1002 ///检验学生信息
#define CMD_GETINFO 1003 ///获取学生信息
#define STAT_OK 30     ///回复OK
#define STAT_ERR 31     ///反复错误
char sendbuf[100];
char recvbuf[100];

typedef struct commu
{
    char name[20];
    int age;
    int cmd;        //命令码
    int stat;
}info;
int main()
{
    int sockfd = -1,ret = -1;
     struct sockaddr_in seraddr = {0};
     struct sockaddr_in cliaddr = {0};

    ///第一步:先socket打开文件描述符
    ///AF_INET表示IPv4,SOCK——STREAM表示TCP协议
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket: .\n");
        return -1;
    }
    printf("soketfd = %d.\n", sockfd);
    ///第二步:connect链接服务器
    seraddr.sin_family = AF_INET;///到底是IPv4还是IPv6,设置地址族
    seraddr.sin_port = htons(MYPORT);///端口号
    seraddr.sin_addr.s_addr = inet_addr(SERADDR);///IP地址
    ret = connect(sockfd, (const struct sockaddr*)&seraddr, sizeof(seraddr));
    if(ret < 0)
    {
        perror("connect");
        return -1;
    }
    printf("成功建立连接\n");
    /*///建立连接后开始通信
    strcpy(sendbuf, "hello world");
    ret = send(sockfd, sendbuf, strlen(sendbuf), 0);
    printf("发送%d个字节\n", ret);*/
/**    while(1)
    {
        ///回合中第一步:客户端给服务器发送信息
        printf("请输入要发送的内容\n");
        scanf("%s", sendbuf);
        ret = send(sockfd, sendbuf, strlen(sendbuf), 0);
        printf("发送了%d个字节\n", ret);

        ///回合中第二步:客户端接收服务器的回复
        memset(recvbuf, 0, sizeof(recvbuf));
        ret = recv(sockfd, recvbuf, sizeof(recvbuf), 0);
        printf("client发送过来的内容是: %s\n", recvbuf);

        ///回合中第三步:客户端解析服务器的回复,再做下一步

    }
**/
    while(1)
    {

        ///回合中第一步:客户端给服务器发送信息
        info st1;
        printf("请输入一个学生姓名:\n");
        scanf("%s", st1.name);
        printf("请输入学生的年龄:\n");
        scanf("%d", &st1.age);
        st1.cmd = CMD_REGISTER;
        ret = send(sockfd, &st1, sizeof(info), 0);
        printf("发送了一个学生信息\n");

        ///回合中第二步:客户端接收服务器的回复
        memset(&st1, 0, sizeof(st1));

        ret = recv(sockfd, &st1, sizeof(st1), 0);
        if(st1.stat == STAT_OK)
        {
            printf("注册学生信息成功。\n");
        }else
        {
            printf("注册学生信息失败。\n");
        }

        ///回合中第三步:客户端解析服务器的回复,再做下一步

    }
    ret = recv(sockfd, sendbuf, sizeof(sendbuf), 0);
    printf("成功接收了%d个字节\n",ret);
    printf("client发送过来的内容是: %s\n", sendbuf);
    return 0;
}

服务器代码

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#define SERADDR "192.168.190.174" ///ifconfig看到的
#define MYPORT 8910
#define BACKLOG 100
#define CMD_REGISTER 1001 ///注册学生信息
#define CMD_CHECK 1002 ///检验学生信息
#define CMD_GETINFO 1003 ///获取学生信息
#define STAT_OK 30     ///回复OK
#define STAT_ERR 31     ///反复错误
char sendbuf[100];
char recvbuf[100];

typedef struct commu
{
    char name[20];
    int age;
    int cmd;        //命令码
    int stat;        //状态信息
}info;
int main(void)
{
     int sockfd = -1,ret = -1,clifd = -1;
     struct sockaddr_in seraddr = {0};
     struct sockaddr_in cliaddr = {0};
     char recvbuf[100];
     socklen_t len = 0;
    ///第一步:先socket打开文件描述符
    ///AF_INET表示IPv4,SOCK——STREAM表示TCP协议
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket: .\n");
        return -1;
    }
    printf("soketfd = %d.\n", sockfd);
    ///第二步:bind绑定socket和当前电脑的ip地址和端口号
    seraddr.sin_family = AF_INET;///到底是IPv4还是IPv6,设置地址族
    seraddr.sin_port = htons(MYPORT);///端口号
    seraddr.sin_addr.s_addr = inet_addr(SERADDR);///IP地址
    ret = bind(sockfd, (const struct sockaddr*)&seraddr, sizeof(seraddr));
    if(ret < 0)
    {
        perror("bind: ");
        return -1;
    }
    printf("bind success.\n");
    ///第三步:listen监听端口
    ret = listen(sockfd, BACKLOG);
    if(ret < 0)
    {
        perror("listen");
        return -1;
    }
    ///第四步:accept阻塞等待客户端
    clifd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
    ///客户端反复给服务器发送
    while(1)
    {
        info st;
        ///回合中第一步:服务器收
        ret = recv(clifd, &st, sizeof(info), 0);
        ///服务器解析客户端数据包
        if(st.cmd == CMD_REGISTER)
        {
            printf("用户注册学生信息");
            printf("姓名:%s\n年龄:%d\n", st.name, st.age);

            ///回合中第三步:回复客户端
            st.stat == STAT_OK;
            ret = send(clifd, &st, sizeof(info), 0);
        }
        if(st.cmd == CMD_CHECK)
        {

        }
        if(st.cmd == CMD_GETINFO)
        {

        }
    }
    ///客户端给服务器发
    return 0;
}

  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值