TCP(Transmission Control Protocol,传输控制协议)是一种常用的网络传输协议,位于网络协议栈的传输层。它为应用程序提供了可靠的、面向连接的数据传输服务。
TCP通过建立连接、数据传输和断开连接等步骤来确保可靠的数据传输。它使用一种称为三次握手的机制来建立连接,其中客户端和服务器通过交换特定的控制信息来协商和确认连接的建立。一旦连接建立,TCP使用序号和确认机制来保证数据的正确性和有序性。
TCP的连接可以分为长连接和短连接,它们在连接的持续时间和使用场景上有所不同。
长连接是指在一次连接中可以连续发送多个请求和响应,而不需要频繁地建立和断开连接。在长连接中,客户端和服务器会保持连接状态一段时间,可以进行多次数据交换。长连接通常适用于客户端和服务器之间需要频繁通信的场景,例如实时通信、即时聊天、流媒体传输等。
优点:
缺点:
短连接是指在完成一次请求和响应后,客户端和服务器立即断开连接。在下一次请求时需重新建立连接。短连接通常适用于客户端和服务器之间交互频率较低或不连续的场景,例如网页浏览、文件下载等。
优点:
缺点:
需要根据具体的应用场景和需求来选择适合的连接方式。一般来说,长连接适合实时通信和频繁交互的场景,短连接适合交互不频繁或不需要实时性的场景。
在Linux中,可以使用Socket和Epoll来编写TCP驱动程序。
Socket是一种编程接口,用于在网络上进行通信。通过Socket API,可以在应用程序中创建、连接、发送和接收数据等操作。使用Socket编写TCP驱动程序的基本步骤如下:
socket()
函数创建一个Socket对象,指定协议族(例如AF_INET,表示IPv4协议)和Socket类型(例如SOCK_STREAM,表示使用TCP协议)。bind()
函数将Socket绑定到特定的IP地址和端口上。listen()
函数开始监听传入的连接请求。accept()
函数接受客户端的连接请求,返回一个新的Socket对象来进行后续的通信。send()
函数发送数据到对方,使用recv()
函数接收对方发送的数据。epoll_create()
函数创建一个Epoll实例。epoll_ctl()
函数将Socket添加到Epoll实例中,关注感兴趣的事件,如可读、可写等。epoll_wait()
函数等待事件发生,此函数会阻塞直到有事件发生。epoll_wait()
返回时,根据返回的事件类型执行相应的操作,如接收数据、发送数据等。通过Socket和Epoll的组合,可以实现TCP驱动程序的开发,包括监听连接、接受请求、发送和接收数据等功能。具体的代码实现需要参考相关的Socket和Epoll的API文档,并根据具体需求编写适当的事件处理逻辑。
#ifndef BSP_TCP
#define BSP_TCP
#ifdef __cplusplus
extern "C" {
#endif
#include "typedef.h"
#include
#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
#include "bsp_tcp.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/***************************** 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;
}
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);