• [LINUX-07]linux网络编程(完整版)


    之间在网上看到很多网络编程都是一个一个demo,今天我把之前学到的汇总起来,希望大家可以进行补充。
    我理解的网络通信分为4种
    1,udp客户端
    2,udp服务端
    3,tcp客户端
    4,tcp服务端

    线程中我使用过两种方式编程,一种是经典函数式编程加上标志位,如下:

    while1)
    {
    server_init();
    client_init();
    sock_send();
    select_handler();
    }

    其中各函数里面放置了大量的标志位,如下:

    void client_init(void)
    {
    //确认客户端初始化标志位
    //
    }
    void sock_send(void)
    {
    //判断客户端标志位,成功则继续进行
    if(client_init_flag)
    {
    //发送操作
    //确认客户端发送标志位
    }
    }
    
    void select_handler(void)
    {
    if(send_flag)
    {
    //处理数据并接受可以用select
    }
    }

    这种方式,我觉得在看代码的时候很乱,但是他在大量的通信时还比较友好,可以建立一个结构体数组,每个数组成员代表一个客户端,结构体放置client_init_flag和send_flag。
    还有一种方式采用的是状态机编程

    创建枚举

    typedef enum client_statues_t
    {
    init_flag_status,
    send_flag_status,
    }client_statues_t;
    client_statues_t client_statues;
    
    while1)
    {
    //先接受,后发送
    switch(client_statues)
    {
    case init_flag_status:
    client_init();
    break;
    case send_flag_status:
    sock_send();
    break; 
    。。。
    
    }
    
    }

    状态机在单片机使用很常见,但是如果多客户端初始化与发送,容易搞混,并且个人觉得增删查改略费劲,有可能是我自己水平有限,所以今天只写了一个关于第一种方式的代码。因为标志位太多了,而且各种判断比较乱,所以以下就没有各种标志位,但真正项目中是要有的并且还需要有打印日志功能(printf函数),整体思路如下:

    >>创建udp服务端,创建tcp服务端
    >>创建udp客户端,创建tcp客户端
    >>发送数据
    >>接受数据并处理

    首先是udp服务端,创建tcp服务端,服务端程序比客户端较简单

    //返回值为是否成功标志,需要在各行赋值代码中判断,此代码不进行演示
    //create_udpServer()、create_tcpServer()参数可以为全局变量的关于服务器端的结构体,结构体里端口号,初始化标志位,等等,此代码不进行演示
    
    struct sockaddr_in udp_sockServer;
    void create_udpServer(void)
    {
    udp_socket = socket(AF_INET, SOCK_DGRAM,0);
    //INADDR_ANY,
    udp_sockServer.sin_addr.s_addr = htonl(INADDR_ANY);
    udp_sockServer.sin_port = htons(udp_port);//port自己设置
    udp_sockServer.sin_family = AF_INET;
    bind(udp_socket, (struct sockaddr *)&udp_sockServer, sizeof(struct sockaddr_in));
    
    }
    struct sockaddr_in tcp_sockServer;
    void create_tcpServer(void)
    {
    tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
    tcp_sockServer.sin_addr.s_addr = htonl(INADDR_ANY);
    tcp_sockServer.sin_port = htons(tcp_podt);//port自己设置
    tcp_sockServer.sin_family = AF_INET;
    bind(tcp_socket, (struct sockaddr *)&tcp_sockServer, sizeof(struct sockaddr_in));
    listen(tcp_socket, n);//n自己设置
    }
    void server_init(void)
    {
    create_udpServer();
    create_tcpServer();
    }

    其次是创建tcp和udp的客户端

    //返回值为是否成功标志,需要在各行赋值代码中判断,此代码不进行演示
    //create_tcpClient()、create_udpClient(v)参数可以为全局变量的关于客户端的结构体,要连接服务器的结构体信息,初始化标志位,等等,此代码不进行演示
    struct sockaddr_in tcpServer;
    void create_tcpClient(void)
    {
    tcpSock = socket(AF_INET, SOCK_STREAM, 0);
    //客户端需要知道服务端信息
    tcpServer.sin_addr.s_addr = htonl(ip);//ip自己设置
    tcpServer.sin_port = htons(tcpPort);//port自己设置
    tcpServer.sin_family = AF_INET;
    /*    设置setsockopt参数 ,具体请看我之间发的一篇blog
    *https://blog.csdn.net/qq_32166779/article/details/88853435
    */
    
    }
    /*把connect单独写是因为这个步骤比较特殊,他是阻塞函数,需要有一定的延时,有两种方、法,一种是利用setsockopt:https://blog.csdn.net/qq_32166779/article/details/88853435
    一种是利用select检查socket描述符:http://blog.csdn.net/ast_224/article/details/2957294
    */
    void tcpClient_connectServer(void)
    {
    //这个tcpsock和create_tcpClient函数中是同一个
    connect(tcpSock, (struct sockaddr *)&tcpServer, sizeof(struct sockaddr_in));
    }
    
    struct sockaddr_in udpServer;
    void create_udpClient(void)
    {
    udpSock = socket(AF_INET, SOCK_DGRAM, 0);
    udpServer.sin_addr.s_addr = htonl(pUDP->ip);
    udpServer.sin_port = htons(pUDP->udpPort);
    udpServer.sin_family = AF_INET;
    }
    
    void client_init(void)
    {
    create_tcpClient();
    tcpClient_connectServer();
    create_udpClient();
    }

    udp 服务器接受与发送函数,这个只设置能接受一个客户端发数

    //udp先接受客户端的数据,然后发送数据
    void udp_server_recviveandsend(void)
    {
    /*
    参数udp_socket为 建立udp服务端的
    */
    recvfrom(udp_socket, rxBuf, rxbuf_len, 0,(struct sockaddr *)&sockClient, &(sizeof(struct sockaddr_in)));
    
    sendto(udp_socket , txBuf, txbuf_len, 0, (struct sockaddr *)&sockClient,sizeof(struct sockaddr_in));
    
    }
    
    
    //tcp客户端发数与接受
    
    void udp_server_recviveandsend(void)
    {
    /*tcpSock为建立*/
    send(tcpSock , txbuf, len, 0);
    recv(tcpSock , rxbuf, len, 0);
    
    }
    
    //tcp服务端是最难的,需要考虑客户端的ip,个数限制,并根据每个客户端进行通信,这里需要设置一个结构体数组,
    
    typedef struct tcp_accept_t
    {
    int lifeNum;//用来设置socket存在时间
    int socket;//
    ulong ip; //保存客户端ip
    ushort port;//保存客户端端口号
    int len;
    }tcp_accept_t;
    #define num 100//定义最大接受客户端的数量,
    tcp_accept_t tcp_accept[num];

    tcp接受客户端大体思路是这样想的:
    1,利用select先获取accept之前的套接字
    2,当accept响应,建立新套接字
    3,有了新套接字,利用select获取receive的响应
    4,如果新来客户端总数超过num,则放弃最早的客户端

    create_tcpServer();
    struct sockaddr_in sockClient;
    while(1)
    {
    /**/
    int socketMax=-1;
    fd_set fdSockSet;
    int fd_act;
    struct timeval timeout;
    
    FD_ZERO(&fdSockSet);
    //tcp_sockServer与create_tcpServer()一致
    if(tcp_sockServer>0)
    {
    FD_SET(tcp_sockServer, &fdSockSet);
    if(socketMax < tcp_sockServer) {socketMax = tcp_sockServer};
    }
    for(int i = 0; i<num;i++)
    {
    if(tcp_accept[i].socket>0)
    {
    FD_SET(tcp_accept[i].socket, &fdSockSet);
    if(socketMax <tcp_accept[i].socket) {socketMax = tcp_accept[i].socket};
    }    
    }
    if(socketMax > 0){
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    fd_act = 0;
    fd_act = select(socketMax+1, &fdSockSet, NULL, NULL, &timeout);
    if(fd_act>0)
    {
    for(int i = 0; i<num;i++)
    {
    if(tcp_accept[i].socket>0)
    {
    FD_SET(tcp_accept[i].socket, &fdSockSet);
    if(FD_ISSET(tcp_accept[i].socket, &fdSockSet)) 
    {
    recv(tcp_accept[i].socket, rxbuf, SOCKET_TCP_RECV, 0);
    }
    }    
    }
    
    if(FD_ISSET(tcp_sockServer, &fdSockSet)) 
    {
    newSocket = -1;
    newSocket = accept(tcp_sockServer, (struct sockaddr *)&sockClient, (socklen_t *)&addrlen);    //sockclient为连接的客户端信息,addrlen为sockaddr结构体长度。    if(newSocket>0){
    ip = ntohl(pTCP->sockClient.sin_addr.s_addr);
    port = ntohs(pTCP->sockClient.sin_port); 
    sLinger.l_onoff = 0;
    ret = setsockopt(newSocket, SOL_SOCKET, SO_LINGER, &sLinger, sizeof(struct linger));
    lifeNumMax++;
    if(lifeNumMax >= 100000000){
    lifeNumMax = 1;
    for(i=0;i<SOCKET_ACCEPT_MAX;i++){
    if(tcpAccept[i].socket > 0){
    tcpAccept[i].lifeNum -= 100000000;
    }
    }    
    }
    /* IP相同,则不需要关闭任何客户端 */
    for (i = 0; i < SOCKET_ACCEPT_MAX; i++) {
    if (tcpAccept[i].socket > 0 && tcpAccept[i].ip == ip) {
    j = i;
    goto accept_new_socket;
    }
    }    
    /* 新来设备,判断是否num不够,如果num够则新增acceptsocket */
    for (i = 0; i < SOCKET_ACCEPT_MAX; i++) {
    if (tcpAccept[i].socket==0) {
    j = i;
    goto accept_new_socket;
    }
    }    
    /* 新来设备,判断是否num不够,如果num不够则新增acceptsocket并放弃最早的客户端 */
    j = 0;    
    lifeNumMin = tcpAccept[0].lifeNum;
    for (i = 1; i < SOCKET_ACCEPT_MAX; i++) {
    if(lifeNumMin > tcpAccept[i].lifeNum){
    lifeNumMin = tcpAccept[i].lifeNum;
    j = i;
    }
    }    
    accept_new_socket:
    
    if(tcpAccept[j].socket > 0){
    close(tcpAccept[j].socket);
    }
    tcpAccept[j].socket = newSocket;
    tcpAccept[j].ip = ip;
    tcpAccept[j].port = port;
    tcpAccept[j].status = 0xff;    /* socket正常使用 */
    tcpAccept[j].lifeNum = lifeNumMax;
    
    
    }
    }
    }

    其中放弃最早的客户端采用了比较简单的算法
    每个客户端在连接时给予一个lifeNum生命值,还有一个不断增加的计数器 lifeNumMax,当客户端增加到最大数量时,比较前几个客户端的生命值,把最小的除去,因为它连接的最久。生命值代码为

    lifeNumMax++;
    if(lifeNumMax >= 100000000){
    lifeNumMax = 1;
    for(i=0;i<SOCKET_ACCEPT_MAX;i++){
    if(tcpAccept[i].socket > 0){
    tcpAccept[i].lifeNum -= 100000000;
    }
    }    
    }

    原文链接:https://blog.csdn.net/qq_32166779/article/details/88861648

  • 相关阅读:
    Vue--路由
    Vue -- 双向过滤器去除html标签
    SQL表的基本操作
    .NET面试题
    MVC Razor
    MVC aspx
    CSS修改滚动条样式
    C# 制作图片验证码
    上传图片加水印
    eclipse常用快捷键
  • 原文地址:https://www.cnblogs.com/Ph-one/p/12739706.html
Copyright © 2020-2023  润新知