C语音实现tcp客户端和tcp服务端,并用Qt调用测试,支持windows和linux跨平台
tcp客户端
创建tcp客户端,在子线程轮询接收数据,接收到的数据通过回调函数传递给mainwindow主线程处理,界面按钮发送tcp数据。
pro工程文件添加
win32{
LIBS += -lws2_32}
tcpclient.h
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <stdint.h>
#include <pthread.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define socklen_t int
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
typedef struct {
int32_t fd;
int32_t localPort;
int32_t remotePort;
int32_t isStart;
int32_t isLoop;
int32_t notRemoteInit;
pthread_t threadId;
int isServer;//0客户端,1服务端
//char serverIp[30];
void* user;
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
void (*receive)(char *data, int32_t nb_data,void* user);
}TcpHandle;
#ifdef __cplusplus
extern "C"{
#endif
int32_t create_tcpClient(TcpHandle* tcpClient,char* serverIp,int32_t serverPort,int32_t plocalPort);
void start_tcpClient(TcpHandle* tcpClient);
void destroy_tcpClient(TcpHandle* tcpClient);
void stop_tcpClient(TcpHandle* tcpClient);
int32_t sendData_tcpClient(TcpHandle* tcpClient, char* data, int32_t len);
#ifdef __cplusplus
}
#endif
#endif // TCPCLIENT_H
tcpclient.c
#include "tcpclient.h"
/**
* @brief create_tcpClient
* @param tcpClient 句柄
* @param serverIp 服务端IP
* @param serverPort
* @param plocalPort
* @return
*/
int32_t create_tcpClient(TcpHandle *tcpClient, char *serverIp, int32_t serverPort, int32_t plocalPort)
{
if (tcpClient == NULL) return -1;
tcpClient->isServer = 0;
tcpClient->localPort = plocalPort;
tcpClient->fd = -1;
tcpClient->remotePort = serverPort;
#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);//初始化socket库
#endif
tcpClient->local_addr.sin_family = AF_INET;
tcpClient->local_addr.sin_port = htons(tcpClient->localPort);
// lcl_addr.sin_addr.s_addr=inet_addr(ip);
tcpClient->local_addr.sin_addr.s_addr = INADDR_ANY;
if((tcpClient->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)//客户端只需要一个套接字文件描述符,用于和服务器通信
{
perror("socket");
return -2;
}
tcpClient->remote_addr.sin_family = AF_INET;
tcpClient->remote_addr.sin_port = htons(tcpClient->remotePort);
tcpClient->notRemoteInit=serverPort>0?0:1;
#ifdef _WIN32
tcpClient->remote_addr.sin_addr.S_un.S_addr=inet_addr(serverIp);
#else
tcpClient->remote_addr.sin_addr.s_addr = inet_addr(serverIp);//将点分十进制IP转换成网络字节序IP
#endif
if(connect(tcpClient->fd, (struct sockaddr *)&tcpClient->remote_addr, sizeof(tcpClient->remote_addr)) < 0)
{
perror("connect");
return -3;
}
return 0;
}
void* recv_thread_tcpClient(void *obj) {
#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
#endif
TcpHandle *ptcpClient = (TcpHandle*) obj;
ptcpClient->isStart = 1;
#ifdef _WIN32
int32_t timeout=200;
setsockopt(ptcpClient->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout, sizeof(timeout));
#else
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 20000; // 20 ms
setsockopt(ptcpClient->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(struct timeval));
#endif
char buffer[2048] = { 0 };
ptcpClient->isLoop = 1;
int32_t len = 0;
socklen_t src_len = sizeof(struct sockaddr_in);
while (ptcpClient->isLoop)
{
struct sockaddr_in src;
memset(&src, 0, src_len);
memset(buffer, 0, 2048);
if ((len = recvfrom(ptcpClient->fd, buffer, 2048, 0, (struct sockaddr*) &src, &src_len)) > 0)
{
if(ptcpClient->notRemoteInit)
{
ptcpClient->remotePort = ntohs(src.sin_port);
ptcpClient->remote_addr.sin_port = src.sin_port;//htons(udp->remotePort);
#ifdef _WIN32
ptcpClient->remote_addr.sin_addr.S_un.S_addr=src.sin_addr.S_un.S_addr;
#else
ptcpClient->remote_addr.sin_addr.s_addr = src.sin_addr.s_addr;
#endif
ptcpClient->notRemoteInit=0;
}
if (ptcpClient->receive) ptcpClient->receive(buffer, len, ptcpClient->user);
}
}
ptcpClient->isStart = 0;
#ifdef _WIN32
closesocket(ptcpClient->fd);
#else
close(ptcpClient->fd);
#endif
ptcpClient->fd = -1;
return NULL;
}
void start_tcpClient(TcpHandle *tcpClient)
{
if (tcpClient == NULL) return;
if (pthread_create(&tcpClient->threadId, 0, recv_thread_tcpClient, tcpClient)) {
printf("could not start thread\n");
}
}
void stop_tcpClient(TcpHandle *tcpClient)
{
if (tcpClient == NULL) return;
tcpClient->isLoop = 0;
while (tcpClient->isStart)
usleep(1000);
}
void destroy_tcpClient(TcpHandle *tcpClient)
{
if (tcpClient == NULL)
return;
stop_tcpClient(tcpClient);
if (tcpClient->fd > 0)
{
#ifdef _WIN32
closesocket(tcpClient->fd);
#else
close(tcpClient->fd);
#endif
tcpClient->fd = -1;
}
}
int32_t sendData_tcpClient(TcpHandle *tcpClient, char *data, int32_t len)
{
if (tcpClient == NULL || !tcpClient->isStart || tcpClient->notRemoteInit || data==NULL) return -1;
return sendto(tcpClient->fd, data, len, 0, (struct sockaddr*) &tcpClient->remote_addr,sizeof(struct sockaddr));
}
QT调用示例
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QThread>
void g_receive(char *data, int32_t nb_data,void *user)
{
if (user == NULL || nb_data <= 0)
return;
MainWindow *mw = (MainWindow*) user;
std::string tmpstr(data,nb_data);
mw->recvTcpData(tmpstr);
return;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
pTcpHandle = (TcpHandle *)malloc(sizeof(TcpHandle));
pTcpHandle->receive = g_receive;
pTcpHandle->user = this;
create_tcpClient(pTcpHandle,"192.168.72.35",60000,0);
start_tcpClient(pTcpHandle);
}
MainWindow::~MainWindow()
{
destroy_tcpClient(pTcpHandle);
QThread::msleep(20);
if(pTcpHandle) free(pTcpHandle);
delete ui;
}
void MainWindow::recvTcpData(std::string str)
{
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"recv data:"<<QString::fromStdString(str);
}
void MainWindow::on_pushButton_clicked()
{
int ret = sendData_tcpClient(pTcpHandle,ui->lineEdit->text().toUtf8().data(),ui->lineEdit->text().size());
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"send data:"<<ret;
}
tcp服务端
创建tcp服务端,在子线程监听,当有客户端连接时,再启动一个子线程负责与该客户端进行数据收发,接收到的数据通过回调函数传递给mainwindow主线程处理,本例是回复一条confirm消息;支持多个客户端同时连接到tcp服务端,与每个客户端的通信是在独立的线程完成。
pro工程文件添加
win32{
LIBS += -lws2_32}
tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <stdint.h>
#include <pthread.h>
#ifdef _WIN32
#define _WIN32_WINNT 0x0600
#include "windows.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#define socklen_t int
#else
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
typedef struct {
int32_t serverfd;
int32_t connFd;
//int32_t lclPort;
int32_t isStart;
int32_t isLoop;
pthread_t threadId;
int32_t serverPort;
void* user;
char remoteIp[32];
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
void (*receive)(char *data, int32_t nb_data,void* user,int32_t clientFd);
void (*startStunTimer)(void* user);
}TcpServer;
#ifdef __cplusplus
extern "C"{
#endif
int32_t create_tcpServer(TcpServer* tcpServer,int32_t listenPort);
void destroy_tcpServer(TcpServer* tcpServer);
void start_tcpServer(TcpServer* tcpServer);
void stop_tcpServer(TcpServer* tcpServer);
int32_t sendData_tcpServer(TcpServer* tcpServer, char* data, int32_t len, int32_t clientFd);
#ifdef __cplusplus
}
#endif
#endif // TCPSERVER_H
tcpserver.c
#include "tcpserver.h"
#ifndef _WIN32
#include <stddef.h>
#define GetSockError() errno
#define SetSockError(e) errno = e
#define closesocket(s) close(s)
#else
#define GetSockError() WSAGetLastError()
#define SetSockError(e) WSASetLastError(e)
#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
#endif
int32_t create_tcpServer(TcpServer *tcpServer, int32_t listenPort)
{
if (tcpServer == NULL)
return 1;
tcpServer->serverfd = -1;
tcpServer->serverPort = listenPort;
tcpServer->local_addr.sin_family = AF_INET;
tcpServer->local_addr.sin_port = htons(listenPort);
#ifdef _WIN32
tcpServer->local_addr.sin_addr.S_un.S_addr=INADDR_ANY;
#else
tcpServer->local_addr.sin_addr.s_addr = INADDR_ANY;
#endif
return 0;
}
void* run_tcpConnClient_thread(void *obj)
{
TcpServer* tcpServer=(TcpServer*)obj;
int connfd=tcpServer->connFd;
char remoteIp[32]={0};
strcpy(remoteIp,tcpServer->remoteIp);
int32_t nBytes =0;
char buffer[2048] = { 0 };
while (tcpServer->isLoop)
{
memset(buffer, 0, 2048);
nBytes = recv(connfd, buffer, 2048, 0);
if (nBytes > 0)
{
tcpServer->receive(buffer, nBytes, tcpServer->user,connfd);
}
else if (nBytes == -1)
{
int32_t sockerr = GetSockError();
if (sockerr == EINTR)
continue;
if (sockerr == EWOULDBLOCK || sockerr == EAGAIN)
{
nBytes = 0;
continue;
}
#ifdef Q_OS_LINUX
printf("%s, recv returned %d. GetSockError(): %d (%s)\n", __FUNCTION__, nBytes, sockerr, strerror(sockerr));
#endif
continue;
}
}
closesocket(connfd);
return NULL;
}
void* run_tcpServer_thread(void *obj) {
#ifdef _WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
#endif
TcpServer *tcpServer = (TcpServer*) obj;
tcpServer->isStart = 1;
tcpServer->serverfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#ifdef _WIN32
int32_t timeout=200;
setsockopt(tcpServer->serverfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout, sizeof(timeout));
#else
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000; // 200 ms
setsockopt(tcpServer->serverfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv,
sizeof(struct timeval));
#endif
printf("\nhttp tcp server is starting,listenPort==%d", tcpServer->serverPort);
if (bind(tcpServer->serverfd, (struct sockaddr*) &tcpServer->local_addr,sizeof(struct sockaddr_in)) < 0) {
printf("http server bind error(%d)",GetSockError());
exit(1);
}
listen(tcpServer->serverfd, 5);
tcpServer->isLoop = 1;
socklen_t src_len = sizeof(struct sockaddr_in);
while (tcpServer->isLoop)
{
struct sockaddr_in src;
memset(&src, 0, src_len);
int connfd = accept(tcpServer->serverfd, (struct sockaddr*) &src, &src_len);
if(connfd>-1)
{
pthread_t th;
tcpServer->connFd=connfd;
memset(tcpServer->remoteIp,0,sizeof(tcpServer->remoteIp));
#ifdef _WIN32
inet_ntop(AF_INET,&src.sin_addr.s_addr, tcpServer->remoteIp, sizeof(tcpServer->remoteIp));
#else
inet_ntop(AF_INET,&src.sin_addr.s_addr, tcpServer->remoteIp, sizeof(tcpServer->remoteIp));
#endif
pthread_create(&th, 0, run_tcpConnClient_thread, tcpServer);
}
}
tcpServer->isStart = 0;
#ifdef _WIN32
closesocket(tcpServer->serverfd);
#else
close(tcpServer->serverfd);
#endif
tcpServer->serverfd = -1;
return NULL;
}
void start_tcpServer(TcpServer *tcpServer)
{
if (tcpServer == NULL) return;
if (pthread_create(&tcpServer->threadId, 0, run_tcpServer_thread, tcpServer))
{
printf("::start could not start thread");
}
}
void destroy_tcpServer(TcpServer *tcpServer)
{
if (tcpServer == NULL)
return;
stop_tcpServer(tcpServer);
if (tcpServer->serverfd > 0) {
closesocket(tcpServer->serverfd);
tcpServer->serverfd = -1;
}
}
void stop_tcpServer(TcpServer *tcpServer)
{
if (tcpServer == NULL)
return;
tcpServer->isLoop = 0;
if(tcpServer->serverfd>0) closesocket(tcpServer->serverfd);
while (tcpServer->isStart)
usleep(1000);
}
int32_t sendData_tcpServer(TcpServer *tcpServer, char *data, int32_t len,int32_t clientFd)
{
if (tcpServer == NULL || !tcpServer->isStart || data==NULL) return -1;
int nBytes = send(clientFd, data, len, 0);
return nBytes;
}
QT调用示例
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QThread>
void g_receive(char *data, int32_t nb_data,void *user,int32_t clientFd) {
if (user == NULL) return;
MainWindow *mw = (MainWindow*) user;
std::string tmpstr(data,nb_data);
mw->recvTcpClientData(tmpstr,clientFd);
printf("clientFd=%d\n",clientFd);
return;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
pTcpServer = (TcpServer *)malloc(sizeof(TcpServer));
pTcpServer->receive = g_receive;
pTcpServer->user = this;
create_tcpServer(pTcpServer,9000);
start_tcpServer(pTcpServer);
}
MainWindow::~MainWindow()
{
destroy_tcpServer(pTcpServer);
QThread::msleep(200);
if(pTcpServer) free(pTcpServer);
delete ui;
}
void MainWindow::recvTcpClientData(std::string str,int32_t clientFd)
{
QString revMsg = QString::fromStdString(str);
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<" "<<revMsg;
QString sendMsg = revMsg + "RecvComfirm";
sendData_tcpServer(pTcpServer,sendMsg.toUtf8().data(),sendMsg.size(),clientFd);
}