1、概述
通常tcp客户端连接服务器,服务器先调用listen处于监听状态,然后客户端调用connect连接服务器,建立连接,对应的3次握手如图:
但是如果不调用listen能否完成3次握手,建立连接呢?答案是可以建立连接的,tcp点对点通信就是这个场景。
tcp实现点对点通信,俩个tcp客户端直接连接,没有服务器,互发数据。不调用listen,accept函数。对应的3次握手图如下:
2、代码如下
思路:
fd = socket();
bind(fd,port);
connect();
send/recv();
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFF_SIZE 1024
typedef int (*RCALLBACK)(int fd);
int epfd = 0;
enum _EPOLL_CTL
{
ADD = 0,
MOD
};
// fd相关事件结构体
struct fd_event {
int fd;
char rbuf[BUFF_SIZE];
int rLen;
char wbuf[BUFF_SIZE];
int wLen;
// 回调函数
union
{
RCALLBACK read_callback;
} r_action;
RCALLBACK send_callback;
} connect_event;
int set_event(int fd, int event, enum _EPOLL_CTL flag)
{
struct epoll_event ev;
ev.events = event;
ev.data.fd = fd;
if (ADD == flag)
{
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
}
else if (MOD == flag)
{
epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
}
}
int recv_cb(int confd)
{
memset(connect_event.rbuf, 0, sizeof(connect_event.rbuf));
int count = recv(confd, connect_event.rbuf, sizeof(connect_event.rbuf), 0);
if (count == 0)
{
epoll_ctl(epfd, EPOLL_CTL_DEL, confd, NULL);
close(confd);
}
connect_event.rLen = count;
printf("Received message: %s\n", connect_event.rbuf);
return count;
}
int send_cb(int confd)
{
int count = send(confd, connect_event.wbuf, connect_event.wLen, 0);
set_event(confd, EPOLLIN, MOD);
return count;
}
void* thread_func(void* arg)
{
int fd = *(int*)arg;
while (1) {
scanf("%s", connect_event.wbuf);
connect_event.wLen = strlen(connect_event.wbuf);
if (strcmp(connect_event.wbuf, "quit") == 0)
{
break;
}
set_event(fd, EPOLLOUT, MOD);
}
close(fd);
}
int main(int argc, char** argv)
{
if (argc <=2 )
{
printf("Usage: %s
exit(1);
}
char* ip = argv[1];
int port = atoi(argv[2]);
printf("ip:%s, port:%d\n", ip, port);
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);// inet_addr("0.0.0.0");//htonl(INADDR_ANY);
addr.sin_port = htons(port); // 0-1023 系统默认端口 1024~65535 用户端口
// 绑定
if(-1 == bind(socketfd, (struct sockaddr*)&addr, sizeof(addr)))
{
printf("bind error errno:%s\n", strerror(errno));
return -1;
}
printf("bind success\n");
epfd = epoll_create(1);
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(struct sockaddr_in));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(ip);//htonl(INADDR_ANY);
serveraddr.sin_port = htons(port); // 0-1023 系统默认端口 1024~65535 用户端口
while (1)
{
if (connect(socketfd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr_in)) == 0) {
printf("connect success errno:%s\n", strerror(errno));
break;
}
//printf("connect....\n");
usleep(1000);
}
connect_event.fd = socketfd;
connect_event.rLen = 0;
connect_event.wLen = 0;
memset(connect_event.rbuf, 0, sizeof(connect_event.rbuf));
memset(connect_event.wbuf, 0, sizeof(connect_event.wbuf));
connect_event.r_action.read_callback = recv_cb;
connect_event.send_callback = send_cb;
set_event(socketfd, EPOLLIN, ADD);
// 启动一个线程处理用户输入
pthread_t tid;
pthread_create(&tid, NULL, thread_func, (void*)&socketfd);
while (1)
{
struct epoll_event events[1024];
int nready = epoll_wait(epfd, events, 1024, -1);
for (int i = 0; i < nready; i++)
{
int confd = events[i].data.fd;
if (events[i].events & EPOLLIN)
{
connect_event.r_action.read_callback(confd);
}
if (events[i].events & EPOLLOUT)
{
connect_event.send_callback(confd);
}
}
}
return 0;
}
3、编译运行
将代码分别拷贝到2个机器上,分别编译
gcc -o p2pClient p2pClient.c -lpthread
在A机器运行:./p2pClient "192.168.202.224" 3000
在B机器运行:./p2pClient "192.168.202.223" 3000
即可互相发送数据
学习链接:https://github.com/0voice