8/12作业

5.4 端口号(重要) ---> port

1> 为了区分同一主机上的多个进程,使用端口号来进行处理

2> 端口号是一个2字节的无符号整数存储,取值范围【0,65535】

3> 网络通信中两个决定性因素:IP + 端口号

4> 端口号的分类:

众所周知的端口号:

0~1023端口我们编程时候不要使用,是那些”VIP“应用程序占了

TCP 21端口:FTP文件传输服务

TCP 23端口:TELNET终端仿真服务

TCP 25端口:SMTP简单邮件传输服务

TCP 110端口:POP3邮局协议版本3

TCP 80端口:HTTP超文本传输服务

TCP 443端口:HTTPS加密超文本传输服务

UDP 53端口:DNS域名解析服务

UDP 69端口:TFTP文件传输服务

特殊的端口函数,存储在linux中的 /etc/services文件中

TCP和UDP的端口号是相互独立的

可以使用的:1024~49151,就是我们平时编写服务器使用的端口号

临时端口号:49152~65535,这部分是客户端运行时候动态选择的

5.5 域名解析

由于使用IP地址来指定计算机不方便人们记忆,且输入时候容易出错,用字符标识网络种计算机名称方法。

这种命名方法就像每个人的名字,这就是域名(Domian Name)

域名服务器(Domain Name server):用来处理IP地址和域名之间的转换。

域名系统(Domain Name System,DNS):域名翻译成IP地址的软件

一个域名,可以绑定多个ip

域名结构

例如域名 http: //www.baidu.com.cn 从右向左看

cn为高级域名,也叫一级域名,它通常分配给主干节点,取值为国家名,cn代表中国

com为网络名,属于二级域名,它通常表示组织或部门

中国互联网二级域名共40个,edu表示教育部门,com表示商业部门,gov表示政府,军队mil等等

baidu为机构名,在此为三级域名,表示百度

www:万维网world wide web,也叫环球信息网,是一种特殊的信息结构框架。

http:使用的是超文本传输协议

一、网络通信之套接字

1.1 套接字通信原理

1.2 socket函数介绍

    #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
       功能:为通信创建一个端点,并返回该端点的文件描述符
       参数1:通信域
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   本地通信,同一主机之间进程通信     详情请看man 7 unix
       AF_INET             IPv4 提供的网络通信               详情请看man 7 ip
       AF_INET6            IPv6 提供的网络通信               详情请看man 7 ipv6
       参数2:指定通信语义,可以由多个宏值使用位或连接
       SOCK_STREAM:表示提供TCP协议的传输方式
       SOCK_DGRAM:表示提供UDP协议的传输方式
       SOCK_NONBLOCK:套接字设置非阻塞属性
       参数3:如果参数2中仅仅指定一个协议,那么参数3可以填0,如果指定多个,则参数3需要指定特定的协议
           TCP协议名称:IPPROTO_TCP
           UDP协议名称:IPPROTO_UDP
       返回值:成功返回创建的套接字文件描述符,失败返回     -1并置位错误码                 
 

二、TCP实现网络通信

2.1 TCP网络通信原理图

2.2 TCP相关函数介绍

1> bind绑定

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);
       功能:位套接字分配名称
       参数1:通过socket函数创建出来的套接字文件描述符
       参数2:通用地址信息结构体,需要根据具体使用的地址族而定, struct sockaddr仅仅只是为了类型的强制转换,防止出现警告
           跨主机间通信:man 7 ip
            struct sockaddr_in {
               sa_family_t    sin_family; /* 表示通信域 */
               in_port_t      sin_port;   /* 端口号的网络字节序 */
               struct in_addr sin_addr;   /* ip地址 */
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* IP地址的网络字节序 */
           };
           同一主机间通信:man 7 uninx
          struct sockaddr_un {
               sa_family_t sun_family;               /* 表示通信域:AF_UNIX */
               char        sun_path[108];            /* 套接字文件的地址 */
           };

        参数3:参数2的大小
        返回值:成功返回0,失败返回-1并置位错误码
 注意关于bind的两个错误:
 1、 Cannot assign requested address:表示IP地址填写错误,检查IP是否有问题
 2、Address already in use:表示地址信息正在占用,可以调用函数快速重用,也可以等一会

2> listen监听

   #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);
       功能:将套接字设置成被动监听状态,已接受客户端的连接请求
       参数1:套接字文件描述符
       参数2:容纳连接的队列的最大长度,一般填128
       返回值:成功返回0,失败返回-1并置为错误码
       
 

3> accept接收连接请求

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
       功能:用于阻塞接收客户端连接请求
       参数1:服务器套接字文件描述符
       参数2:用于接收对端地址信息结构体的指针
       参数3:接收对端地址信息的长度
       返回值:成功返回一个新的用于通信的套接字文件描述符,失败返回-1并置位错误码
 

4> recv接收数据


       ssize_t recv(int sockfd, void *buf, size_t len, int flags);
       功能:从套接字中读取数据到buf中
       参数1:用于通信的套接字文件描述符
       参数2:接收数据后的容器地址
       参数3:接收的数据的大小
       参数4:是否阻塞接收
              0:表示阻塞接收消息
              MSG_DONTWAIT:表示非阻塞接收数据
        返回值:
            >0:表示成功读取的字符个数
            =0:表示通信对端已经下线
            =-1:表示出错,置位错误码                    

5> send发送数据

   #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);
       功能:向通信套接字文件描述符中写入数据
       参数1:通信的套接字文件描述符
       参数2:要发送数据的起始地址
       参数3:要发送数据的大小
       参数4:是否阻塞接收
              0:表示阻塞接收消息
              MSG_DONTWAIT:表示非阻塞接收数据
       返回值:成功返回发送字符的个数,失败返回-1并置位错误码    

6> connect连接函数

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);
       功能:将套接字文件描述符连接到addr指向的地址空间中
       参数1:客户端套接字文件描述符
       参数2:对端地址信息结构体
       参数3:参数2的大小
       返回值:成功返回0,失败返回-1并置位错误码

2.3 TCP服务器端代码实现

#include<myhead.h>
#define SER_PORT 6666          //服务器端口号
#define SER_IP "192.168.0.130"    //服务器ip地址


int main(int argc, const char *argv[])
{
    //1、创建套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    //参数1:表示ipv4的网络通信
    //参数2:表示使用的是TCP通信方式
    //参数3:表示默认使用一个协议
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("socket success, sfd = %d\n", sfd);        //3

    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in sin;       
    sin.sin_family = AF_INET;       //通信域
    sin.sin_port = htons(SER_PORT);    //端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);    //ip地址

    //2.2 绑定工作
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    //3、将套接字设置成被动监听状态
    if(listen(sfd, 128)==-1)
    {
        perror("listen error");
        return -1;
    }
    printf("listen success\n");

    //4、阻塞等待客户端的连接请求
    //4.1 定义变量用于接收客户端的信息
    struct sockaddr_in cin;          //用于接收地址信息
    socklen_t addrlen = sizeof(cin);  //用于接收长度
    
    //4.2 接收连接
    int newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen) ;
    if(newfd == -1)
    {
        perror("accept error");
        return -1;
    }
    printf("[%s:%d]: 已成功连接一个客户端\n", \
            inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

    //5、数据收发
    char buf[128] = "";

    while(1)
    {
        //清空容器
        bzero(buf, sizeof(buf));

        //从客户端套接字中读取数据
        int res = recv(newfd, buf, sizeof(buf), 0);
        if(res == -1)
        {
            perror("read error");
            return -1;
        }else if(res == 0)
        {
            printf("客户端已经下线\n");
            close(newfd);             //关闭客户端套接字
            break;
        }

        //正常收到客户端发来的消息
        printf("[%s:%d] : %s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);

        //对收到的消息进行一顿操作
        //此处省略一万行代码
        strcat(buf, "*_*");

        //将消息回复给客户端
        if(send(newfd, buf, strlen(buf), 0) == -1)
        {
            perror("发送error");
            return -1;
        }
        printf("发送成功\n");
    }

    //6、关闭监听
    close(sfd);

    return 0;
}

2.4 TCP服务器通信模型

1、sfd = socket();                //创建一个用于连接的套接字文件描述符
2、bind();                        //为服务器套接字绑定ip地址和端口号,为了让客户端额能够找到服务器
3、listen();                       //将服务器套接字设置成被动监听状态,用于接收客户端的连接请求
4、newfd = accept();              //阻塞等待客户端的连接请求,如果有客户端发来连接请求,创建一个新的用于通信的套接字文件描述符
5、while(1)
{
    send\recv\read\write;           //数据收发工作
}
6、close();                              //关闭套接字、关闭监听

2.5 TCP客户端实现

#include<myhead.h>

#define SER_PORT 6666             //与服务器保持一致
#define SER_IP  "192.168.0.130"    //服务器ip地址
#define CLI_PORT 8888               //客户端端口号
#define CLI_IP  "192.168.0.130"     //客户端ip地址


int main(int argc, const char *argv[])
{
    //1、创建用于通信的套接字文件描述符
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("cfd = %d\n", cfd);             //3
    
    //2、绑定IP地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in cin;       
    cin.sin_family = AF_INET;       //通信域
    cin.sin_port = htons(CLI_PORT);    //端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //ip地址

    //2.2 绑定工作
    if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    
    //3、连接到服务器
    //3.1 填充服务器地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;          //通信域
    sin.sin_port = htons(SER_PORT);      //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);     //服务器ip地址

    //3.2 连接服务器
    if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("connect error");
        return -1;
    }
    printf("连接服务器成功\n");
    
    //4、数据收发
    char buf[128] = "";
    while(1)
    {
        printf("请输入>>>");
        fgets(buf, sizeof(buf), stdin);         //从终端获取一个字符串
        buf[strlen(buf)-1] = 0;

        //将数据发送给服务器
        send(cfd, buf, strlen(buf), 0);
        printf("发送成功\n");

        //接受服务器发来的数据
        //清空容器
        bzero(buf, sizeof(buf));
        recv(cfd, buf, sizeof(buf), 0);
        printf("收到服务器消息为:%s\n", buf);
    }
    
    //5、关闭套接字
    close(cfd);

    return 0;
}

三、UDP实现网络通信

3.1 UDP网络通信模型

3.2 UDP相关函数

  ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
       //功能:从套接字文件描述符中读取数据,并将对端地址信息结构体接收
       参数1:套接字文件描述符
       参数2:要接收数据的起始地址
       参数3:要接收的数据大小
       参数4:是否阻塞,0表示阻塞,MSG_NOWAIT表示非阻塞
       参数5:接收对端地址信息结构体
       参数6:参数5的大小
       返回值:成功返回读取的字节的大小,失败返回-1并置位错误码
       
        ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
      //功能:向套接字文件描述符中读取数据,写给指定的对端接收
       参数1:套接字文件描述符
       参数2:要发送数据的起始地址
       参数3:要发送的数据大小
       参数4:是否阻塞,0表示阻塞,MSG_NOWAIT表示非阻塞
       参数5:接收对端地址信息结构体
       参数6:参数5的大小
       返回值:成功返回发送的字节的大小,失败返回-1并置位错误码

3.3 UDP服务器端实现

#include<myhead.h>

#define SER_PORT 9999          //服务器端口号
#define SER_IP "192.168.0.130"   //服务器ip地址

int main(int argc, const char *argv[])
{
    //1、创建用于通信的服务器套接字文件描述符
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("sfd = %d\n", sfd);      //3


    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in sin;       
    sin.sin_family = AF_INET;       //通信域
    sin.sin_port = htons(SER_PORT);    //端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);    //ip地址

    //2.2 绑定工作
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    
    //3、数据收发
    char buf[128] = "";
    struct sockaddr_in cin;            //接受对端地址信息
    socklen_t addrlen = sizeof(cin);   //接受地址长度

    while(1)
    {
        //清空容器
        bzero(buf, sizeof(buf));

        //从套接字中读取数据
        recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
        printf("收到消息为:%s\n", buf);

        //将收到的消息进行处理
        //此处省略一万行
        strcat(buf, "*_*");

        if(sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cin, sizeof(cin)) == -1)
        {
            perror("发送error");
            return -1;
        }
        printf("发送成功\n");
    }
    

    //4、关闭文件描述符
    close(sfd);

    return 0;
}

3.4 UDP客户端实现

#include<myhead.h>

#define SER_PORT 9999          //服务器端口号
#define SER_IP "192.168.0.130"   //服务器ip地址
#define CLI_PORT 5555             //客户端端口号
#define CLI_IP  "192.168.0.130"    //客户端ip地址

int main(int argc, const char *argv[])
{
    //1、创建用于通信的服务器套接字文件描述符
    int cfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("sfd = %d\n", cfd);      //3


    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in cin;       
    cin.sin_family = AF_INET;       //通信域
    cin.sin_port = htons(CLI_PORT);    //端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //ip地址

    //2.2 绑定工作
    if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    
    //3、数据收发
    char buf[128] = "";
    //填充服务器的地址信息结构体
    struct sockaddr_in sin;            //接受对端地址信息
    sin.sin_family = AF_INET;      //服务器的通信域
    sin.sin_port = htons(SER_PORT);    //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);     //服务器ip地址

    while(1)
    {
        //从终端获取数据
        printf("请输入>>>>");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf)-1] = 0;

        //将数据发送给服务器
        sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&sin, sizeof(sin));

        printf("发送成功\n");

        //清空容器
        bzero(buf, sizeof(buf));
        //接受服务器发来的消息
        recvfrom(cfd, buf, sizeof(buf), 0, NULL, NULL);
        printf("收到服务器消息为:%s\n", buf);

    }
    

    //4、关闭文件描述符
    close(cfd);

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值