栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

在两个私网客户端上如何实现TCPsocket通信,使用云服务器作为中转客户端

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

在两个私网客户端上如何实现TCPsocket通信,使用云服务器作为中转客户端

目录

文章目录

前言

一、TCP为什么不能直接在两个私网客户端之间通信?

二、使用步骤

1.申请阿里云esc云服务器

2.在两个客户端运行客户端代码

三、运行服务端客户端代码

总结


前言

本人最近在学习TCP通信的时候发现TCP只能实现在局域网下任意两个客户端的通信,但是对于非局域网内的客户端,并不能直接访问,查询了众多资料后,终于找到了解决办法,能够实现任意两个或者多个非局域网客户端的相互通信,在此介绍给大家!


一、TCP为什么不能直接在两个私网客户端之间通信?

这就设计到网络通信的原理了,在网络通信中,任意两个局域网的设备之间是可以直接访问的,但是如果两台设备不在同一个局域网下,是不能直接访问的。但是如果其中一台设备拥有公网域名或者公网ip,他们两个之间就可以直接访问。根据这个原理,我们可以借用一台拥有公网IP的设备作为中转站服务器,然后服务器接收客户端A的信息,再将客户端A的信息转发给客户端B,实现了非局域网TCP通信。

二、使用步骤

1.申请阿里云esc云服务器

 在阿里云官网申请ESC云服务器,新用户可以免费试用七天。然后配置阿里云服务器,首先设置安全组,如下图所示:

 配置完云服务器后可以直接通过远程登陆登陆到阿里云服务器上,第一次登陆可能会让你设置密码账号,界面如下:

 然后我们将服务端代码移植到服务器上就可以了

如果你能够在局域网下实现TCP通信以及多线程的相关操作,那么一下代码是能够看懂的。

有几个关键点总结一下:

1.服务端的代码中的IP地址要填阿里云服务器的公网IP地址。

2.这里的原理是通过多线程实现的,当检测到有客户端连接时,通过pthread_create()函数创建子进程与客户端通信,父进程依旧在检测等待第二个客户端连接。

3.服务端是如何区分两个客户端的?是通过accept()函数的返回值描述符来区分的,通过将客户端返回的描述符存放在数组c_fd[2]中,然后通过write()函数分别发送。

代码如下(示例):

#include "stdio.h"
#include "stdlib.h"
#include "assert.h"
#include "string.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include "netinet/in.h"
#include "unistd.h"
#include "pthread.h"



int ret;
int i=0;
int c_fd[2];
struct sockaddr_in saddr,caddr;
pthread_t t1;
pthread_t t2;
char BUFF1[256];
char BUFF2[256];
int read_1;
int read_2;
int param=100;



void *thread1(void *arg)
{
while(1)

{
memset(BUFF1,0,sizeof(BUFF1));
printf("Client1 address is %sn",inet_ntoa(caddr.sin_addr));
read_1=read(c_fd[0],BUFF1,255);
if(read_1==-1) {printf("read client1 is error");}
else { printf("I read client1:%sn",BUFF1);
write(c_fd[1],BUFF1,strlen(BUFF1));
}

}

}

void *thread2(void *arg)
{

while(1)
{
memset(BUFF2,0,sizeof(BUFF2));
printf("client2 address is %sn",inet_ntoa(caddr.sin_addr));
read_2=read(c_fd[1],BUFF2,255);
if(read_2==-1) {printf("read client2 is error");}
else { printf("I read client2:%sn",BUFF2);
write(c_fd[0],BUFF2,strlen(BUFF2));
}

}

}


int main()
{



        int sockfd=socket(AF_INET,SOCK_STREAM,0);
        assert(sockfd!=-1);
//      struct sockaddr_in saddr,caddr;
        memset(&saddr,0,sizeof(saddr));
        saddr.sin_family=AF_INET;
        saddr.sin_port=htons(8000);
        saddr.sin_addr.s_addr=inet_addr("172.17.124.48");
        int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
        assert(res!=-1);
        res=listen(sockfd,5);
        assert(res!=0);

        int len=sizeof(caddr);


        while(1)
        {
           if(i==0)
           {
           c_fd[i]=accept(sockfd,(struct sockaddr*)&caddr,&len);
                if(c_fd[i]<0){continue;}
i=1;
                printf("I am client1n");
           }else {

              c_fd[i]=accept(sockfd,(struct sockaddr*)&caddr,&len);
             if(c_fd[i]<0){continue;}
              i=2;
              printf("I am client2n");
         }

 if(c_fd[0]==-1)
      {printf("client1 is connect error");}
        else
        { ret=pthread_create(&t1,NULL,thread1,(void *)¶m);
        assert(ret!=-1);
        }

if(c_fd[1]==-1)
        {printf("client2 is connect error");}
        else
        { ret =pthread_create(&t2,NULL,thread2,(void *)¶m);assert(res!=-1);}





        }

        return 0;
}

2.在两个客户端运行客户端代码

这里的代码与同一局域网下的代码几乎没有什么区别,主要在于IP地址需要更改,这里的IP要填阿里云服务器的内网IP,记住!是内网IP,然后其他几乎没有什么变化,端口号也要一致。

代码如下(示例):

#include "stdio.h"
#include "stdlib.h"
#include "sys/socket.h"
#include "sys/types.h"
#include "unistd.h"
#include "assert.h"
#include "arpa/inet.h"
#include "string.h"
#include "netinet/in.h"


int main()
{

	
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
printf("socket=%dn",sockfd);
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(8000);
saddr.sin_addr.s_addr=inet_addr("39.103.227.125");
int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
while(1)
{

printf("please input:n");
char buff[255]={0};

fgets(buff,255,stdin);
if(strncmp(buff,"end",3)==0){break;}
send(sockfd,buff,strlen(buff)-1,0);

char date[255]={0};
int n=recv(sockfd,date,254,0);
printf("i recive to aliyun:%sn",date);



}

close(sockfd);
}

第二个客户端的代码就不给出了,与这个几乎没有区别

三、运行服务端客户端代码

先运行云服务器上面的客户端代码,再运行两个客户端代码

左上角为虚拟机,左下角为另一局域网下的树莓派,右边为阿里云服务器,现在连接成功后服务端成功打印


 通过实例可以看出代码正常运行,能够达到我们的要求效果

总结

这个公网TCP通信头疼了我好几天,因为一直不知道服务端如何区分客户端,因为在父进程和子进程中套接字和端口号都是一样的,后来在翻阅资料发现可以通过accept()函数的描述符区分客户端,然后成功编写了如下代码,效果跟预想一样。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/880159.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号