网络编程小结(基本架构,无知识点总结)

一、TCP客户服务器编程模型

  1. 客户端

  • 调用socket函数创建套接字。

int socket(int domain, int type, int protocol);

头文件:#include<sys/socket.h>

参数说明:

socket:指出协议族(如AF_INET指定IPV4)

type:指定协议类型,SOCK_STREAM指定字节流协议(如TCP),SOCK_DGRAM指定数据报协议(如UDP)。

protocol:指定具体协议,通常传0

返回值:成功返回非负的 套接字,失败返回-1

  • 调用connect连接服务器端。

int  connect(int sockfd, struct sockaddr *serv_addr, int addrlen );

头文件:#include<sys/types.h>

#include<sys/socket.h>

参数说明:

sockfd: socket函数返回的套接字

serv_addr: 服务器地址

IPV4地址族
#include <netinet/in.h>
struct in_addr {
    in_addr_t s_addr;
};
struct sockaddr_in {
uint16_t  sin_family;//地址族,如AF_INET, 主机字节序。
uint16_t   sin_port;//端口号,16位值,网络字节序。

struct  in_addr sin_addr;//IPv4地址,一个32位整数,网络字节序。
char    sin_zero[8];
};

addrlen: sockaddr结构体的长度

返回值:成功返回0,失败返回-1并设置errno

  • 调用I/O函数(read/write)与服务器端通讯。

int read (int sockfd, void* buf, size_t nbytes);
int write(int sockfd, void*buf, size_t nbytes);

头文件:#icnlude<unistd.h>

参数说明:

sockfd:已连接套接字

*buf:保存数据的缓冲区

nbytes:期望写入(或写出)的字节数

返回值:返回读或者写的字节数,失败返回-1并设置errno

  • 调用close关闭套接字。

int close(int sockfd);

头文件: #include<unistd.h>

参数说明:

sockfd:socket函数返回的套接字

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



void hangle(int sig)
{
    printf("recv  sig: %d\n", sig);
}




int main()
{

    signal(SIGPIPE, hangle);
    int sockfd, ret;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in  seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(8000);
    inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
    ret = connect(sockfd, (struct sockaddr*)&seraddr, sizeof(struct sockaddr_in))    ;
    if(ret < 0)
    {
        perror("connect");
        return  -1;
    }


    char buff[1024];
    while(1)
    {
        printf("请输入数据:");
        scanf("%s", buff);
        ret = write(sockfd, buff, strlen(buff));
        if(ret<0)
        {
            perror("write");
            return -1;
        }
    }

    close(sockfd);

    return 0;
}
  1. 服务器

  • 调用socket函数创建套接字。

同客户端

  • 调用bind指定本地地址和端口。

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

头文件:#include<sys/types.h>

#include<sys/soccket.h>

参数说明:

sockfd:socket函数返回的套接字

*addr:本地端口和地址

addrlen:addr的字节数

返回值:成功返回0不成功返回-1,并把错误码存放在全局变量errno中

一台主机可以有多个网络接口和多个IP地址,如果我们只关心某个地址的连接请求,我们可以指定一个具体的本地IP地址
#define INADDR_ANY (uint32_t)0x00000000
这个地址可以响应所有接口上的连接请求
  • 调用listen启动监听。

int listen(int sockfd, int backlog);

头文件:#include<sys/socket.h>

参数说明:

sockfd :成功调用socket函数返回的套接字,并已经成功调用bind。

backlog: 告诉套接字在忙于处理上一个请求时还可以接受多少个进入的请求,换句话说,这决定了挂起连接的队列的大小.

返回值:成功返回0,失败返回-1并将错误码存放与全局变量errno中。

  • 调用accept从已连接列队中提取客户连接。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

头文件:#include<sys/socket.h>

参数说明:

sockfd:成功调用socket()返回的套接字

addr:接收连接客户端的地址

addrlen:addr的长度

返回值:成功返回客户端套接字,失败返回-1并将错误码存放与全局变量errno中。

一般与connect对称使用
  • 调用I/O函数(read/write)与客户端通讯。

同客户端

  • 调用close关闭连接。

同客户端

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

#define   SERADDR    "127.0.0.1"
#define   SERPORT    8000

//成功返回监听套接字, 失败返回-1
int sockfd_init()
{
    int sockfd, ret;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }

    //设置端口复用
    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));


    struct sockaddr_in  seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERPORT);
    inet_pton(AF_INET, SERADDR, &seraddr.sin_addr.s_addr);
    ret = bind(sockfd, (struct  sockaddr*)&seraddr, sizeof(struct sockaddr_in));
    if(ret<0)
    {
        perror("bind");
        return -1;
    }

    ret  = listen(sockfd, 20);
    if(ret < 0)
    {
        perror("listen");
        return  -1;
    }

    return sockfd;
}


int main()
{
    int sockfd, ret;
    int cfd, efd, count;

    sockfd = sockfd_init();
    if(sockfd<0)
    {
        return -1;
    }

    efd = epoll_create(10);
    if(efd < 0)
    {
        perror("epoll_create");
        return -1;
    }
    struct epoll_event  ev, evs[10];
    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &ev);


    char buff[1024];
    while(1)
    {
        printf("wait...\n");
        count = epoll_wait(efd, evs, 10, -1);
        printf("wait  over...\n");
        if(count<0)
        {
            perror("epoll_wait");
            break;
        }

        for(int i=0; i<count; i++)
        {
            int temp = evs[i].data.fd;
            if(temp == sockfd) //说明有客户端请求连接
            {
                //1、接收客户端
                printf("accept...\n");
                cfd = accept(sockfd, NULL, NULL);
                printf("accept  over...\n");
                if(cfd<0)
                {
                    perror("accept");
                    continue;
                }
                //2、cfd加入efd集合中
                ev.data.fd = cfd;
                epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &ev);
            }
            else //说明已经连接的客户端发来数据
            {
                printf("Read...\n");
                ret = read(temp, buff, 1024 );
                printf("Read  over...\n");
                if(ret< 0)
                {
                    //1、打印错误信息
                    perror("read");
                    //2、关闭文件描述符
                    close(temp);
                    //3、从集合中移除
                    epoll_ctl(efd, EPOLL_CTL_DEL, temp, NULL);
                }
                else if(0 == ret)
                {
                    //1、打印错误信息
                    printf("tcp  broken...\n");
                    //2、关闭文件描述符
                    close(temp);
                    //3、从集合中移除
                    epoll_ctl(efd, EPOLL_CTL_DEL, temp, NULL);

                }

                buff[ret] = '\0';
                printf("buff: %s\n", buff);
            }
        }
    }

    return 0;
}

三、epoll、select

在 Linux 中,epoll 是一种高效的多路复用机制,比 select 更快。epoll 是基于事件驱动的机制,可以同时监视大量的文件描述符,并且支持水平触发和边缘触发两种工作方式。相比于 select,epoll 有以下优点:
无限制的文件描述符:epoll 可以监视数十万个文件描述符,而 select 和 poll 机制的可监视文件描述符数量通常受到系统限制。
更高效的事件通知:epoll 只会通知发生变化的文件描述符,而 select 会通知所有监视的文件描述符,需要应用程序自行处理哪些文件描述符发生了变化。
更快的速度:epoll 在大量文件描述符的情况下,比 select 更快,因为 select 在每次调用时都需要遍历所有的监视对象,而 epoll 可以只通知有变化的对象。
更少的内存使用:epoll 使用的内存比 select 少,因为 epoll 会维护一个事件表,而 select 会维护一个监视对象列表。
  1. epoll

头文件:#include<sys/epoll.h>

  • 根据文件描述符的个数创建空间

 int epoll_create(int size);

参数:size:设置监听文件描述符的个数

返回值:成功返回与集合空间关联的文件描述符,失败返回-1;

  • 控制集合空间的文件描述符

 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数说明:

epfd:和集合关联的文件描述符

op:操作命令

EPOLL_CTL_ADD:往集合控件添加文件描述符
EPOLL_CTL_DEL:从集合空间删除文件描述符的命令

fd:操作的文件描述符

event:如果是删除操作,可直接忽略,默认传NULL;

如果是添加操作,通过参数告诉内核监听某个文件描述符的某个事件

 typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;      /*设置监听的事件:  EPOLLIN(读事件) */
               epoll_data_t data;        /* data.fd :设置监听文件描述符*/
           };
  • 监听集合中文件描述符

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

参数说明:

epfd:和集合关联的文件描述符

events:接受准备文件描述符信息数组的起始地址

maxevents:数组的最大元素个数

timeout:设置超时时间(以毫秒为单位)

>0:设置超时时间
=0:该函数是一个非阻塞函数
-1:永久阻塞

返回值:>0:准备好的文件描述符个数

=0:超时解除阻塞

-1:出错

  1. select

头文件:#include<sys/select.h>或#include<sys/time

int select(int nfds, fd_set *readfds, fd_set *writefds,
        fd_set *exceptfds, struct timeval *timeout);

参数说明:

nfds: 监听最大文件描述符+1

readfds: 监听读集合事件的起始地址

writefds: 监听写集合事件的起始地址,

exceprfds: 监听异常集合事件的起始地址,

timeout: 设置超时时间

struct timeval {
                long    tv_sec;         /* seconds */
                long    tv_usec;        /* microseconds */
                };

返回值:

>0: 有响应文件描述符的个数

=0:超时时间到

-1: 出错

void FD_CLR(int fd, fd_set *set); //将fd从set集合中删除
int FD_ISSET(int fd, fd_set *set); //判断fd是否在set集合中, 在返回真,否则返回假
void FD_SET(int fd, fd_set *set); // 将fd加入set集合中
void FD_ZERO(fd_set *set); //清空set集合
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
    int  maxfd, ret;

    //1、创建集合
    fd_set  set, rset;

    //2、将监听文件描述符加入set集合
    FD_SET(0, &set);

    //3、设置最大值
    maxfd = 0;

    char buff[1024];

    struct timeval  tv;
    tv.tv_sec = 2;
    tv.tv_usec = 0;

    while(1)
    {
        rset = set;
        //4、调用select函数监听集合
        printf("select...\n");
        ret = select(maxfd+1, &rset, NULL, NULL, &tv);
        printf("select  over...\n");
        if(ret<0)
        {
            perror("select");
            break;
        }

        if(0 == ret)
        {
            printf("time  out...\n");
            tv.tv_sec = 2;
            tv.tv_usec = 0;
            continue;
        }

        //5、采用暴力的方式找出有响应的文件描述符
        for(int i=0; i<=maxfd; i++)
        {
            if(FD_ISSET(i, &rset))
            {
                printf("read...\n");
                ret = read(i, buff, 1024);
                printf("read  over...\n");
                buff[ret] = '\0';
                printf("buff: %s\n", buff);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网络编程基本概念和相关知识包括以下内容: 1. IP 地址:IP(Internet Protocol)地址是用于在网络上唯一标识设备的地址。IP 地址分为 IPv4 和 IPv6 两种格式,IPv4 地址由四个字节组成,而 IPv6 地址由八个字节组成,用于实现设备之间的通信。 2. 端口号:端口号用于标识网络应用程序中的进程或服务,范围是从 0 到 65535。其中,0 到 1023 是被标准化的端口号,用于常见的网络服务,如 HTTP(端口号80)、FTP(端口号21)等。 3. 套接字:套接字(Socket)是网络编程中的一种抽象概念,用于描述网络通信的一端。套接字提供了一组方法和属性,用于建立、发送和接收数据。 4. TCP 和 UDP:TCP(Transmission Control Protocol)和 UDP(User Datagram Protocol)是两种常见的网络传输协议。TCP 提供可靠的、面向连接的通信,确保数据的有序性、完整性和可靠性;UDP 则是无连接的通信协议,适用于实时性要求较高的场景。 5. 客户端和服务器:在网络通信中,客户端是发起连接请求的一方,服务器是接受请求并提供服务的一方。客户端和服务器之间通过套接字进行通信。 6. HTTP 和 HTTPS:HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议,常用于 Web 应用程序中。HTTPS 是基于 HTTP 的安全传输协议,使用了 SSL/TLS 加密来保护数据的安全性。 7. 网络安全性:网络安全性涉及到数据传输的保密性、完整性和身份验证等问题。常见的网络安全技术包括加密算法、数字证书、防火墙、访问控制等。 以上是网络编程的一些基本概念和相关知识,了解这些内容可以帮助你理解和应用网络编程技术。在实际开发中,还需要深入学习和实践,以更好地应对各种网络通信需求和挑战。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值