江湖上一直都有这位哥哥的传说,也有很多人说自己就他的真身!
但是。。。
今天分享一下TCP连接的P2P demo,江湖的规矩也要与时俱进。。。
————————————————————————————————————
原理之步骤(原理说了也不是那个理,做NAT的不说,谁知道呢?还不如过程呢)
一.A,B连接打洞服务器S
二.S记录下A的公网IP和端口,记录下B的公网
IP和端口
三.S将A的公网IP和端口以及B的公网端口发给B
四.S将B的公网IP和端口发给A
五. S命令B向A的公网IP和端口发起connect(打洞)
六. S命令B在B的公网端口上监听A的连接(connect)
七.S命令A向B的公网IP和端口发出连接命令(connect
// server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
typedef struct IP_PORT
{
char ip[64];
int port;
}IP_PORT;
void sigint(int signum)
{
if (SIGINT==signum)
{
exit(1);
}
}
int main()
{
int listenfd;
int value = 1;
socklen_t clilen;
struct sockaddr_in cliaddr, seraddr;
int sock_a, sock_b;
char tmpaddr[128], tmpaddr_port[128], tmpaddr_port_port[128];
signal(SIGINT, sigint);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1==listenfd)
{
printf("socket -server error: %s\n", strerror(errno));
return 1;
}
// reuse socket's address
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &value, sizeof(value));
// set bind server
memset(&seraddr, 0, sizeof(seraddr));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
seraddr.sin_port = htons(8099);
if (-1==bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr)))
{
printf("bind -server error: %s\n", strerror(errno));
close(listenfd);
return 1;
}
if (-1==listen(listenfd, 5))
{
printf("listen -server error: %s\n", strerror(errno));
close(listenfd);
return 1;
}
sock_a = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
if (-1==sock_a)
{
printf("accept -server error: %s\n", strerror(errno));
close(listenfd);
return 1;
}
inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, tmpaddr, sizeof(tmpaddr));
sprintf(tmpaddr_port, "%s %d", tmpaddr, ntohs(cliaddr.sin_port));
sock_b = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
if (-1==sock_b)
{
printf("accept -server error: %s\n", strerror(errno));
close(sock_a);
close(listenfd);
return 1;
}
sprintf(tmpaddr_port_port, "%s %d", tmpaddr_port, ntohs(cliaddr.sin_port));
write(sock_b, tmpaddr_port_port, strlen(tmpaddr_port_port)+1);
close(sock_b);
printf("cli_a IP PORT cli_b PORT: %s\n", tmpaddr_port_port);
inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, tmpaddr, sizeof(tmpaddr));
sprintf(tmpaddr_port, "%s %d", tmpaddr, ntohs(cliaddr.sin_port));
write(sock_a, tmpaddr_port, strlen(tmpaddr_port)+1);
close(sock_a);
printf("cli_b IP PORT: %s\n", tmpaddr_port);
close(listenfd);
printf("exit -server\n");
return 0;
}
//cli_a.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
typedef struct IP_PORT
{
char ip[64];
int port;
}IP_PORT;
void sigint(int signum)
{
if (SIGINT==signum)
{
exit(1);
}
}
int main(int argc, char **argv)
{
struct sockaddr_in seraddr;
int sockfd;
char tmpaddr_port[128];
IP_PORT peer;
int len;
char tmpbuf[128];
signal(SIGINT, sigint);
if (2!=argc)
{
printf("CmdLine should be: cli_a <IP>\n");
return 1;
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1==sockfd)
{
printf("socket -cli_a error: %s\n", strerror(errno));
return 1;
}
memset(&seraddr, 0, sizeof(seraddr));
seraddr.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &seraddr.sin_addr);
seraddr.sin_port = htons(8099);
if (0>connect(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr)))
{
printf("connect main -cli_a error: %s\n", strerror(errno));
return 1;
}
if (0>read(sockfd, tmpaddr_port, 128))
{
printf("read main -cli_a error: %s\n", strerror(errno));
return 1;
}
sscanf(tmpaddr_port, "%s %d", peer.ip, &peer.port);
// close main socket
close(sockfd);
// create new p2p client socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1==sockfd)
{
printf("socket -cli_a error: %s\n", strerror(errno));
return 1;
}
inet_pton(AF_INET, peer.ip, &seraddr.sin_addr);
seraddr.sin_port = htons(peer.port);
printf("Peer cli_b IP PORT: %s %d\n", peer.ip, peer.port);
// wait for cli_b to punch hole to cli_a self and cli_b is in accepting
sleep(5);
if (0>connect(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr)))
{
printf("connect p2p -cli_a error: %s\n", strerror(errno));
close(sockfd);
return 1;
}
len = write(sockfd, "I am a\n", strlen("I am a\n")+1);
if (0>=len)
{
printf("write p2p -cli_a error: %s\n", strerror(errno));
close(sockfd);
return 1;
}
len = read(sockfd, tmpbuf, 128);
if (0<len)
{
printf("%s", tmpbuf);
printf("\n");
}
close(sockfd);
printf("exit -cli_a\n");
return 0;
}
//cli_b.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
typedef struct IP_PORT
{
char ip[64];
int port;
}IP_PORT;
void sigint(int signum)
{
if (SIGINT==signum)
{
exit(1);
}
}
int main(int argc, char **argv)
{
struct sockaddr_in seraddr, cliaddr;
socklen_t clilen;
int sockfd, listenfd;
unsigned int value = 1;
char tmpaddr_port_port[128];
IP_PORT peer;
int len;
char tmpbuf[128];
int port;
signal(SIGINT, sigint);
if (2!=argc)
{
printf("CmdLine should be: cli_b <IP>\n");
return 1;
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1==sockfd)
{
printf("socket -cli_b error: %s\n", strerror(errno));
return 1;
}
memset(&seraddr, 0, sizeof(seraddr));
seraddr.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &seraddr.sin_addr);
seraddr.sin_port = htons(8099);
if (0>connect(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr)))
{
printf("connect main -cli_b error: %s\n", strerror(errno));
return 1;
}
if (0>read(sockfd, tmpaddr_port_port, 128))
{
printf("read main -cli_b error: %s\n", strerror(errno));
return 1;
}
sscanf(tmpaddr_port_port, "%s %d %d", peer.ip, &peer.port, &port);
// close main socket
close(sockfd);
// create new p2p connect/listen socket
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1==listenfd)
{
printf("socket p2p connect -cli_b error: %s\n", strerror(errno));
return 1;
}
inet_pton(AF_INET, peer.ip, &seraddr.sin_addr);
seraddr.sin_port = htons(peer.port);
printf("Peer cli_a IP PORT: %s %d\n", peer.ip, peer.port);
// punch hole to cli_a
connect(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr));
printf("Punching hole is finished!\n");
// reuse socket's address
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &value, sizeof(value));
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
seraddr.sin_port = htons(port);
if (0>bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr)))
{
printf("bind p2p -cli_b error: %s\n", strerror(errno));
close(listenfd);
return 1;
}
if (0>listen(listenfd, 5))
{
printf("listen p2p -cli_b error: %s\n", strerror(errno));
close(listenfd);
return 1;
}
sockfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen);
if (-1==sockfd)
{
printf("accept p2p -cli_b error: %s\n", strerror(errno));
close(listenfd);
return 1;
}
len = read(sockfd, tmpbuf, 128);
if (0<len)
{
printf("%s", tmpbuf);
printf("\n");
}
len = write(sockfd, "I am b\n", strlen("I am b\n")+1);
if (0>=len)
{
printf("write p2p -cli_b error: %s\n", strerror(errno));
close(sockfd);
close(listenfd);
return 1;
}
close(sockfd);
close(listenfd);
printf("exit -cli_b\n");
return 0;
}
//result
Finally:
这下,有需要的朋友去p2p吧!!!