前言
本篇内容是对已实现的通信程序进行模块化封装以及将服务端改为多进程并发通信,将初始化sokcet和recv与send操作封装成类,套接字描述符与通信实现的具体细节对用户隐藏。
实现方法参照 https://www.freecplus.net/44e059cca66042f0a1286eb188c51480.html
通信服务端
服务端main主函数
/*
* 此程序演示socket通信服务
* 作者:zhy 时间:2020 4 4
*/
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <arpa/inet.h>
// 声明服务端socket通信操作类
CTcpServer TcpServer;
void EXIT(int sig);
int main()
{
//忽略全部信号
for (int ii = 0;ii < 50;++ii) signal(ii,SIG_IGN);
//设置SIGINT和SIGTERM的信号处理函数
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
// 初始化服务端,设置监听端口
if (TcpServer.InitServer(5000) == false)
{ printf("服务端初始化失败,程序退出。\n"); return -1; }
while(1)
{
if ( TcpServer.Accept() == false) continue;
// 父进程关闭用于通信的socket(对子进程无影响)回到循环开始继续监听连接
if (fork() > 0) { TcpServer.CloseClient(); continue; }
// 子进程关闭用于监听连接的socket,只与客户端通信
TcpServer.CloseListen();
printf("客户已连接。\n");
//与客户端通信,接受到客户端发送的报文后,回复ok
char buffer[1024];
unsigned int a;
while(1)
{
memset(buffer,0,sizeof(buffer));
if(TcpServer.Recv(&a,sizeof(long)) <= 0) break;
printf("接收:%d\n",ntohl(a));
strcpy(buffer,"ok");
if(TcpServer.Send(buffer,sizeof(buffer)) <= 0) break;
printf("发送:%s\n",buffer);
}
printf("客户已断开连接。\n");
return 0; //子进程退出
}
}
//SIGINT和SIGTERM信号处理函数
void EXIT(int sig)
{
printf("\n程序退出sig=%d\n",sig);
TcpServer.~CTcpServer(); //手动调用析构函数,释放资源
exit(0);
}
服务端操作类
class CTcpServer
{
public:
int m_listenfd; // 服务端用于监听的socket
int m_clientfd; // 客户端连上来的socket
CTcpServer();
bool InitServer(const int port); //初始化服务端
bool Accept(); //等待客户端的连接
//向对端发送报文
int Send(const void *buf,const int buflen);
//接收对端的报文
int Recv(void *buf,const int buflen);
void CloseClient(); //关闭客户端的socket
void CloseListen(); //关闭用于监听的socket
~CTcpServer();
};
CTcpServer::CTcpServer()
{
m_listenfd = 0;
m_clientfd = 0;
}
CTcpServer::~CTcpServer()
{
if(m_listenfd != 0) close(m_listenfd);
if(m_clientfd != 0) close(m_clientfd);
}
bool CTcpServer::InitServer(const int port) //初始化服务端
{
m_listenfd = socket(AF_INET,SOCK_STREAM,0); // 创建服务端用于监听的socket
//把服务端用于通信的地址和端口绑定到socket上
struct sockaddr_in servaddr; // 服务器地址信息的数据结构
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET; // 协议族,在socket编程中只能是AF_INET
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 本主机的任意IP地址
//servaddr.sin_addr.s_addr = inet_addr("192.168.1.12"); // 绑定指定的地址
servaddr.sin_port = htons(port); // 绑定通信端口(数值可改)
if (bind(m_listenfd ,(struct sockaddr *)&servaddr,sizeof(servaddr)) != 0 )
{ close(m_listenfd); m_listenfd = 0; return false; }
//把socket设置为监听模式
if (listen(m_listenfd,5) != 0 ) { close(m_listenfd); m_listenfd = 0; return false; }
return true;
}
bool CTcpServer::Accept() //等待客户端的连接
{
if ((m_clientfd=accept(m_listenfd,0,0)) <= 0) return false;
return true;
}
//向对端发送报文
int CTcpServer::Send(const void *buf,const int buflen)
{
return send(m_clientfd,buf,buflen,0);
}
//接收对端的报文
int CTcpServer::Recv(void *buf,const int buflen)
{
memset(buf,0,buflen);
return recv(m_clientfd,buf,buflen,0);
}
void CTcpServer::CloseClient() //关闭客户端的socket
{
if(m_clientfd!=0) { close(m_clientfd); m_clientfd=0;}
}
void CTcpServer::CloseListen() //关闭用以监听的socket
{
if(m_listenfd!=0) { close(m_listenfd); m_listenfd=0;}
}
通信客户端
客户端main函数
/*
* 此程序演示socket通讯客户端
* 作者:zhy 时间:2020 4 4
*/
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
CTcpClient TcpClient;
//向服务器发起连接请求
if (TcpClient.ConnectToServer("192.168.150.142",5000) == false)
{ printf("连接服务器失败,程序退出。\n"); return -1; }
char strbuffer[1024];
unsigned int a = 14664464;
unsigned int b = htonl(a);
unsigned int c = ntohl(b);
printf("a = %ld\nb = %ld\nc = %ld\n",a,b,c);
//与服务端通讯,发送一个报文后等待回复,然后再发下一个报文
for (int ii =0;ii<30;ii++)
{
/*
memset(strbuffer,0,sizeof(strbuffer));
sprintf(strbuffer,"这是第%d个报文",ii);
*/
if (TcpClient.Send(&b,sizeof(long)) <= 0) break;
printf("发送:%d\n",b);
if (TcpClient.Recv(strbuffer,sizeof(strbuffer)) <= 0) break;
printf("接收:%s\n",strbuffer);
sleep(1);
}
}
客户端操作类
class CTcpClient
{
private:
int m_sockfp;
public:
CTcpClient();
//向服务器发起连接,Serverip-服务端ip ,port通讯端口
bool ConnectToServer(const char* Serverip,const int port);
//向对端发送报文
int Send(const void *buf,const int buflen);
//接收对端的报文
int Recv(void *buf,const int buflen);
~CTcpClient();
//向服务器发起连接,Serverip-服务端ip ,port通讯端口
bool CTcpClient::ConnectToServer(const char* Serverip,const int port)
{
m_sockfp = socket(AF_INET,SOCK_STREAM,0); // 创建客户端的socket
struct hostent* h; //IP地址信息的数据结构
if ( (h = gethostbyname(Serverip)) == 0 )
{ close(m_sockfp); m_sockfp = 0; return false; }
//把服务器的地址和端口转换为数据结构
struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
memcpy(&servaddr.sin_addr,h->h_addr,h->h_length);
//向服务器发起连接请求
if ( connect(m_sockfp,(struct sockaddr*)&servaddr,sizeof(servaddr)) != 0 )
{ close(m_sockfp); m_sockfp = 0; return false; }
return true;
}
//向对端发送报文
int CTcpClient::Send(const void *buf,const int buflen)
{
return send(m_sockfp,buf,buflen,0);
}
//接收对端的报文
int CTcpClient::Recv(void *buf,const int buflen)
{
memset(buf,0,buflen);
return recv(m_sockfp,buf,buflen,0);
}
CTcpClient::CTcpClient()
{
m_sockfp = 0;
}
CTcpClient::~CTcpClient()
{
if (m_sockfp != 0) close(m_sockfp);
}
};