Linux网络通讯(简单TCP/UDP服务器)

网络通讯

TCP:

Transmission Control Protocol 传输控制协议TCP是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议,在 OSI模型中,它完成第四层传输层所指定的功能。

UDP:

UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,IETF RFC 768是UDP的正式规范。

在网络通讯时我们需要用到套接字Socket
网络通讯的相关的函数讲解:https://blog.csdn.net/wfea_lff/article/details/104113640
在之前已经说过这里就不过说了,直接进入主题

一、TCP

下图为TCP基本的服务器过程

通过上图,首先我们知道服务器肯定是可以多人同时连接也可同时发送数据,通过TCP来看,建立连接后,服务器与客户端不断进行着(客户端)发数据和(服务器)收数据,而且可以允许多个客户端同时连接服务器想到这里我们肯定会有一个想法那就是用循环实现不断地accept客户端,在循环实现不断地accept客户端中又使用循环来实现(客户端)发数据和(服务器)收数据

我们先来看看这样的代码片段

......
while(1)
    {
        int len = sizeof(struct sockaddr);
        int accept_ret = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
        char buf[128];
    	while(1)
    	{
       		memset(buf,0,128);
        	int recv_ret = recv(accept_ret,buf,128,0);
        	printf("client(%s) send to server is %s",inet_ntoa(cliaddr.sin_addr),buf);
        	if(strncmp(buf,"end",3) == 0)
        	{
            	break;
        	}
    	}
    }
......

从上面的代码中我们可以看出,通过循环的嵌套来实现的服务器,我们不难看出这种方法有着很致命的缺陷,这种方法确实实现了数据的发送和接收,但他并不能实现多个客户端同时连接服务器,因为如果一个客户端在不断的发数据,那他就一直在内层循环中不断的运行,导致accept不能连接下一个客户端,而只有等连接的客户端断开连接的时侯,下一个客户端才能连接服务器,这显然不符合服务器的特点,所以这种方法是不对的

那我们应该怎样实现呢
这里,我们不难想到其实可以通过多线程的方式实现,通过另一个线程来完成功能
我们还是先来看看代码片段

void *fun(void *arg)
{
    char buf[128];
    while(1)
    {
        memset(buf,0,128);
        int recv_ret = recv((int)arg,buf,128,0);
        printf("client(%s) send to server is %s",inet_ntoa(cliaddr.sin_addr),buf);
        if(strncmp(buf,"end",3) == 0)
        {
            break;
        }
    }
    close((int)arg);
}
.
.
.
while(1)
    {
        int len = sizeof(struct sockaddr);
        int accept_ret = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
        pthread_t id;
        pthread_create(&id,NULL,fun,(void *)accept_ret);
    }

这次从上面的代码中我们可以看出,当一个客户端accept后这个程序就会分两路同时运行,一个去进行fun函数,一个继续循环accept来接受客户端的连接,而fun则就实现了(客户端)发数据和(服务器)收数据,两边互不干扰,实现了服务器可以多人同时连接也可同时发送数据

二、UDP

UDP的服务器实现就简单了很多,因为UDP是无连接 不可靠 数据报,UDP不需要listen和accept,发送数据的函数是sendto,接收用的是recvfrom,发送数据的函数里面的参数包含着服务器的地址和端口和要发送的数据,只要客户端不断的发送数据,UDP的服务器便能不断的接收,且可以连接多个客户端。

下图是简单的流程
在这里插入图片描述

参考代码

一、TCP

服务器

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

#define BACKLOG 2

int sockfd;
struct sockaddr_in cliaddr;

void *fun(void *arg)
{
    char buf[128];
    while(1)
    {
        memset(buf,0,128);
        int recv_ret = recv((int)arg,buf,128,0);
        if(recv_ret == -1)
        {
            printf("server recv message fail\n");
            return ;
        }
        else
        {
            printf("server recv message success\n");
        }
        printf("client(%s) send to server is %s",inet_ntoa(cliaddr.sin_addr),buf);
        if(strncmp(buf,"end",3) == 0)
        {
            break;
        }
    }
    close((int)arg);
}

int main()
{
    sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
        printf("creat socket fail\n");
        return -1;
    }
    else
    {
        printf("creat socket success , sockfd = %d\n",sockfd);
    }

    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(5000);
    seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int bind_ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(struct sockaddr));
    if(bind_ret == -1)
    {
        printf("bind fail\n");
        close(sockfd);
        return -2;
    }
    else
    {
        printf("bind success\n");
    }

    int listen_ret = listen(sockfd,BACKLOG);
    
    if(listen_ret == -1)
    {
        printf("listen socket fail\n");
        close(sockfd);
        return -3;
    }
    else
    {
        printf("listen socket success\n");
    }

    while(1)
    {
        int len = sizeof(struct sockaddr);
        int accept_ret = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
        if(accept_ret == -1)
        {
            printf("accept fail\n");
            close(sockfd);
            return -4;
        }
        else
        {
            printf("accept success , accept_ret = %d\n",accept_ret);
        }

        pthread_t id;
        pthread_create(&id,NULL,fun,(void *)accept_ret);
    }

    close(sockfd);
    
    return 0;
}

客户端

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

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
        printf("creat socket fail\n");
        return -1;
    }
    else
    {
        printf("creat socket success , sockfd = %d\n",sockfd);
    }

    struct sockaddr_in cliadd;
    cliadd.sin_family = AF_INET;
    cliadd.sin_port = htons(5000);
    cliadd.sin_addr.s_addr = inet_addr("127.0.0.1");

    int connect_ret = connect(sockfd,(struct sockaddr *)&cliadd,sizeof(struct sockaddr));
    if(connect_ret == -1)
    {
        printf("connect server fail\n");
        close(sockfd);
        return -2;
    }
    else
    {
        printf("connect server success\n");
    }
    
    char buf[128];
    while(1)
    {
        memset(buf,0,128);
        printf("send to server's message :\n");
        fgets(buf,128,stdin);
        send(sockfd,buf,strlen(buf),0);
        if(strncmp(buf,"end",3) == 0)
        {
            break;
        }
    }

    close(sockfd);
    
    return 0;
}

二、UDP

服务器

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

int main()
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        printf("creat socket fail\n");
        return -1;
    }
    else
    {
        printf("creat socket success , sockfd = %d\n",sockfd);
    }

    struct sockaddr_in seraddr,cliaddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(5000);
    seraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    int bind_ret = bind(sockfd,(struct sockaddr *)&seraddr,sizeof(struct sockaddr));
    if(bind_ret == -1)
    {
        printf("bind fail\n");
        close(sockfd);
        return -2;
    }
    else
    {
        printf("bind success\n");
    }

    char buf[128];
    int len = sizeof(struct sockaddr);
    while(1)
    {
        memset(buf,0,128);
        recvfrom(sockfd,buf,128,0,(struct sockaddr *)&cliaddr,&len);
        printf("client(%s) send message : %s",inet_ntoa(cliaddr.sin_addr),buf);
    }

    close(sockfd);
    return 0;
}

客户端

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

int main()
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        printf("creat socket fail\n");
        return -1;
    }
    else
    {
        printf("creat socket success , sockfd = %d\n",sockfd);
    }

    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(5000);
    seraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    char buf[128];
    int len = sizeof(struct sockaddr);
    while(1)
    {
        memset(buf,0,128);
        printf("input message : \n");
        fgets(buf,128,stdin);
        sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&seraddr,len);
        if(strncmp(buf,"end",3) == 0)
        {
            break;
        }
    }

    close(sockfd);

    return 0;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值