1.TCP简介
TCP(Transmission Control Protocol,传输控制协议)是一种常用的网络传输协议,位于网络协议栈的传输层。它为应用程序提供了可靠的、面向连接的数据传输服务。
TCP通过建立连接、数据传输和断开连接等步骤来确保可靠的数据传输。它使用一种称为三次握手的机制来建立连接,其中客户端和服务器通过交换特定的控制信息来协商和确认连接的建立。一旦连接建立,TCP使用序号和确认机制来保证数据的正确性和有序性。
TCP的连接可以分为长连接和短连接,它们在连接的持续时间和使用场景上有所不同。
1.1长连接
长连接是指在一次连接中可以连续发送多个请求和响应,而不需要频繁地建立和断开连接。在长连接中,客户端和服务器会保持连接状态一段时间,可以进行多次数据交换。长连接通常适用于客户端和服务器之间需要频繁通信的场景,例如实时通信、即时聊天、流媒体传输等。
优点:
- 减少了建立和断开连接的开销,节省网络资源和时间。
- 提供了持久连接,实现了即时通信和实时数据传输。
缺点:
- 长时间保持连接可能导致服务器资源占用过多。
- 可能增加服务器的负载和管理复杂性。
1.2 短连接
短连接是指在完成一次请求和响应后,客户端和服务器立即断开连接。在下一次请求时需重新建立连接。短连接通常适用于客户端和服务器之间交互频率较低或不连续的场景,例如网页浏览、文件下载等。
优点:
- 每个请求结束后立即释放资源,减少服务器负载。
- 简化服务器管理和资源分配。
缺点:
- 每次建立连接都需要消耗额外的时间和资源。
- 对于频繁交互的应用,建立和断开连接带来的开销可能较大。
需要根据具体的应用场景和需求来选择适合的连接方式。一般来说,长连接适合实时通信和频繁交互的场景,短连接适合交互不频繁或不需要实时性的场景。
2.linux中tcp编程思路
在Linux中,可以使用Socket和Epoll来编写TCP驱动程序。
2.1使用Socket
Socket是一种编程接口,用于在网络上进行通信。通过Socket API,可以在应用程序中创建、连接、发送和接收数据等操作。使用Socket编写TCP驱动程序的基本步骤如下:
- 创建Socket:使用
socket()
函数创建一个Socket对象,指定协议族(例如AF_INET,表示IPv4协议)和Socket类型(例如SOCK_STREAM,表示使用TCP协议)。 - 绑定地址:使用
bind()
函数将Socket绑定到特定的IP地址和端口上。 - 监听连接:使用
listen()
函数开始监听传入的连接请求。 - 接受连接:使用
accept()
函数接受客户端的连接请求,返回一个新的Socket对象来进行后续的通信。 - 发送和接收数据:使用
send()
函数发送数据到对方,使用recv()
函数接收对方发送的数据。
2.2使用Epoll:
- Epoll是Linux内核提供的一种事件通知机制,可以高效地监听文件描述符的状态变化。使用Epoll编写TCP驱动程序的基本步骤如下:
- 创建Epoll实例:使用
epoll_create()
函数创建一个Epoll实例。 - 添加Socket到Epoll:使用
epoll_ctl()
函数将Socket添加到Epoll实例中,关注感兴趣的事件,如可读、可写等。 - 等待事件:使用
epoll_wait()
函数等待事件发生,此函数会阻塞直到有事件发生。 - 处理事件:当
epoll_wait()
返回时,根据返回的事件类型执行相应的操作,如接收数据、发送数据等。
通过Socket和Epoll的组合,可以实现TCP驱动程序的开发,包括监听连接、接受请求、发送和接收数据等功能。具体的代码实现需要参考相关的Socket和Epoll的API文档,并根据具体需求编写适当的事件处理逻辑。
3.头文件
#ifndef BSP_TCP
#define BSP_TCP
#ifdef __cplusplus
extern "C" {
#endif
#include "typedef.h"
#include <sys/epoll.h>
#define MAXEPOLLSIZE 100
#define MAX_BUF_SIZE 1024 * 10
typedef struct TCPServerStatus
{
int epfd;
int listenFd; //Server fd
bool stopFlag;
int clientFd; //Client fd currently in use
struct epoll_event events[MAXEPOLLSIZE];
} TCPServerStatus;
bool tcp_server_init(TCPServerStatus *server, unsigned port);
bool tcp_writeComm(TCPServerStatus *server, unsigned char *data, int dataLen);
int tcp_readComm(TCPServerStatus *server, int fd, char *data, int dataLen);
int tcp_getClientCnt(TCPServerStatus *server);
void server_tcp_do_accept(TCPServerStatus *server, int listenFd, int epfd);
bool checkIsClientFd(int currentFd, int clientFd);
bool close_tcp_client(TCPServerStatus *server);
#ifdef __cplusplus
}
#endif
#endif
4.实现cpp
#include "bsp_tcp.h"
#include <errno.h>
#include <stdbool.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/resource.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
/***************************** TCP *****************************/
static int setnonblocking(int sockfd)
{
/* Set non-blocking */
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK) == -1) {
printf("set nonblock fail\n");
return -1;
}
return 0;
}
static void setSocketOption(int sockfd)
{
int sendBufSize = 1024*1024;
/* optimize increase send buffer */
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char*)&sendBufSize, sizeof(int));
}
bool close_tcp_client(TCPServerStatus *server)
{
if(server->clientFd < 0)
return true;
struct epoll_event ev;
ev.data.fd = server->clientFd;
epoll_ctl(server->epfd, EPOLL_CTL_DEL, server->clientFd, &ev);
/* Close and reset the receive buffer */
close(server->clientFd);
server->clientFd = -1;
SQFdebug("close_tcp_client close! \r\n");
return true;
}
int create_tcp_listen(ushort port)
{
int sockFd, ret;
struct sockaddr_in seraddr;
bzero(&seraddr, sizeof(struct sockaddr_in));
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = INADDR_ANY;
seraddr.sin_port = htons(port);
sockFd = socket(AF_INET, SOCK_STREAM, 0);
if (sockFd < 0)
{
perror("socket error");
return -1;
}
{
/* Setting Address Reuse */
int bReuseaddr = 1;
/* Increase the sending buffer */
setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(int));
}
/* Set non-blocking */
if (setnonblocking(sockFd) < 0)
{
perror("setnonblock error");
}
/* optimize */
setSocketOption(sockFd);
ret = bind(sockFd, (struct sockaddr*)&seraddr, sizeof(struct sockaddr));
if (ret < 0)
{
perror("bind");
return -1;
}
ret = listen(sockFd, 5); //max 5
if (ret < 0)
{
perror("listen");
return -1;
}
return sockFd;
}
void server_tcp_do_accept(TCPServerStatus *server, int listenFd, int epfd)
{
struct sockaddr_in cliaddr;
socklen_t socklen = sizeof(struct sockaddr_in);
struct epoll_event ev;
if(server->clientFd > 0)
{
/* If a TCP connection exists */
close_tcp_client(server);
}
server->clientFd = accept(listenFd, (struct sockaddr*)&cliaddr, &socklen);
if (server->clientFd < 0)
{
perror("accept");
return;
}
if (setnonblocking(server->clientFd) < 0)
{
perror("setnonblocking error");
return;
}
{
int bufferSize = 0;
socklen_t len = sizeof(bufferSize);
getsockopt(server->clientFd, SOL_SOCKET, SO_SNDBUF, &bufferSize, &len);
SQFdebug("server_tcp_do_accept accept %d buf:%d! \r\n", server->clientFd, bufferSize);
}
ev.events = EPOLLIN;
ev.data.fd = server->clientFd;
epoll_ctl(epfd, EPOLL_CTL_ADD, server->clientFd, &ev);
}
bool tcp_server_init(TCPServerStatus *server, unsigned port)
{
int ret = 0;
struct epoll_event ev;
server->clientFd = -1;
server->epfd = epoll_create(MAXEPOLLSIZE);
if (server->epfd < 0)
{
perror("epoll create");
return false;
}
server->listenFd = create_tcp_listen(port);
if (server->listenFd < 0)
{
printf("listen and bind.\n");
return false;
}
ev.events = EPOLLIN;
ev.data.fd = server->listenFd;
ret = epoll_ctl(server->epfd, EPOLL_CTL_ADD, server->listenFd, &ev);
if (ret < 0)
{
perror("epoll_ctl");
return false;
}
/* Process SIGPIPE
* (this signal will appear if the client is closed but still sending data to the client) */
signal(SIGPIPE, SIG_IGN);
return true;
}
bool tcp_writeComm(TCPServerStatus *server, unsigned char *data, int dataLen)
{
ssize_t n = 0;
int retry = 10;
while((n = send(server->clientFd, (char *)data, dataLen, 0)) != (ssize_t)dataLen)
{
if(errno == 0)
{
SQFdebug("MSG_SendMsgTo send incomplete %d:%d\n", dataLen, n);
}
else if(errno == EAGAIN)
{
SQFdebug("MSG_SendMsgTo send overflow\n");
}
else
{
SQFdebug("MSG_SendMsgTo send\n");
break;
}
if(--retry < 0)
break;
}
return true;
}
int tcp_readComm(TCPServerStatus *server, int fd, char *data, int dataLen)
{
int len;
/* Only the data from the current TCP client is processed */
if(fd != server->clientFd)
{
SQFdebug("unexpected client, fd = %d.\n", fd);
close(fd);
}
len = recv(fd, data, dataLen, 0);
if (len == 0)
{
/* client closed */
close_tcp_client(server);
SQFdebug("a client disconnect, fd = %d.\n", fd);
}
else if (len < 0 && errno != EAGAIN)
{
SQFdebug("recv error:%s\n", strerror(errno));
}
return len;
}
int tcp_getClientCnt(TCPServerStatus *server)
{
if (server->stopFlag)
{
SQFdebug("g_server's stopFlag is set to true, exit\n");
return -1;
}
int ready = epoll_wait(server->epfd, server->events, MAXEPOLLSIZE, 1000);
if (ready < 0)
{
if(errno != EINTR)
{
perror("epoll_wait.");
return -1;
}
}
return ready;
}
bool checkIsClientFd(int currentFd, int clientFd)
{
if(currentFd != clientFd)
{
SQFdebug("unexpected client, fd = %d.\n", currentFd);
close(currentFd);
return false;
}
return true;
}
5.示例
TcpComm::TcpComm(unsigned short port)
{
tcp_server_init(&com, port);
}
TcpComm::~TcpComm()
{
}
bool TcpComm::sendData(unsigned char *data, int dataSize)
{
return tcp_writeComm(&com, data, dataSize);
}
void TcpComm::dataReceived()
{
int ready = tcp_getClientCnt(&com);
if(ready > 0)
{
printf("ready = %d\n", ready);
for (int i = 0; i < ready; i++)
{
if (com.events[i].data.fd == com.listenFd)
{
server_tcp_do_accept(&com, com.listenFd, com.epfd);
}
else
{
/* Only the data from the current TCP client is processed */
if(checkIsClientFd(com.events[i].data.fd, com.clientFd) == false)
{
continue;
}
int byteRead = tcp_readComm(&com, com.events[i].data.fd, buf, len);
if (byteRead == 0)
{
/* client closed */
close_tcp_client(&com);
printf("a client disconnect, fd = %d.\n", com.events[i].data.fd);
}
else if (byteRead < 0)
{
printf("recv error\n");
}
else if(byteRead > 0)
{
//todo something
}
}
}
}
}
#define TcpPort 5000
TcpComm *comm = new TcpComm(TcpPort);