linux网络编程

本文介绍了网络编程中的UDP和TCP客户端与服务器的创建过程,包括初始化、发送和接收数据。通过经典函数式编程和状态机编程两种方式对比,探讨了各自在多客户端场景下的优缺点。同时,展示了如何处理多客户端连接,特别是在TCP服务器中限制并发连接数量的方法。
摘要由CSDN通过智能技术生成

微信可以设置雪花昵称了,真漂亮!!!

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

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

while(1) { server_init(); client_init(); sock_send(); select_handler(); }

1 2 3 4 5 6 7 8 9 10 其中各函数里面放置了大量的标志位,如下:

void client_init(void) { //确认客户端初始化标志位 // } void sock_send(void) { //判断客户端标志位,成功则继续进行再见,优酷!再见,爱奇艺! if(client_init_flag) { //发送操作 //确认客户端发送标志位 } }

void select_handler(void) { if(send_flag) { //处理数据并接受可以用select } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 这种方式,我觉得在看代码的时候很乱,但是他在大量的通信时还比较友好,可以建立一个结构体数组,每个数组成员代表一个客户端,结构体放置client_init_flag和send_flag。 还有一种方式采用的是状态机编程

创建枚举 typedef enum client_statues_t { init_flag_status, send_flag_status, }client_statues_t; client_statues_t client_statues; 1 2 3 4 5 6 7 while(1) { //先接受,后发送 switch(client_statues) { case init_flag_status: client_init(); break; case send_flag_status: sock_send(); break;
。。。

    }

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

创建udp服务端,创建tcp服务端 创建udp客户端,创建tcp客户端

发送数据 接受数据并处理

1 2 3 4 5 首先是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(); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 其次是创建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(); }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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));

}

1 2 3 4 5 6 7 8 9 10 11 12 //tcp客户端发数与接受

void udp_server_recviveandsend(void) { /tcpSock为建立/ send(tcpSock , txbuf, len, 0); recv(tcpSock , rxbuf, len, 0);

} 1 2 3 4 5 6 7 //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];

1 2 3 4 5 6 7 8 9 10 11 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;

​ } }

} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 其中放弃最早的客户端采用了比较简单的算法 每个客户端在连接时给予一个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;
            }
        }        
    }

———————————————— 版权声明:本文为CSDN博主「当霸气遇到侧漏」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_32166779/article/details/88861648

以上就是良许教程网为各位朋友分享的Linux相关知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值