前言:
在完成了TCP的服务端Demo之后,现在开始尝试开发客户端方面的项目。在做客户端之前先想下,客户端和服务端是不是公用一些类,头文件,创建socket,关闭socket,recv,send。这些客户端和服务端应该是公用的。应该放在XTcp.h,XTcp.cpp中。如果我们直接把这些文件加入到我们的项目中,这个方案非常不灵活,每次编译项目的时候,都要加一大推文件。还要重新编译这部分代码。如果解决这个问题呢?这里提出一个动态库的概念。
本博客重点将linux下将服务器的建立绑定连接socket功能接口的过程。
如下代码:
创建so的makefile
libxsocket.so:XTcp.h XTcp.cpp g++ $+ -o $@ -fpic -shared -std=c++11
编译生成动态链接库的生成文件XTcp.cpp
#include "XTcp.h" #ifdef WIN32 #include#define socklen_t int #else #include #include #include #include #define closesocket close #endif #include #include #include #include using namespace std; XTcp::XTcp() { #ifdef WIN32 static bool first = true; if (first) { first = false; //保证只进入一次 WSADATA ws; WSAStartup(MAKEWORd(2, 2), &ws); //相当于加载了动态库,给引用增加1,这个只需要调用以此,但是需要调用在最前面 } #endif } int XTcp::CreateSocket() //第一步创建socket { sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { printf("create socket failed!n"); } return sock; } bool XTcp::Bind(unsigned short port) { if (sock <= 0) CreateSocket(); //创建 sockaddr_in saddr; saddr.sin_family = AF_INET; //协议 saddr.sin_port = htons(port); //本地字节序转换为网络字节序 saddr.sin_addr.s_addr = htonl(0); //任意ip地址发过来的数据都接收 //绑定 //用bind绑定,绑定哪一个端口 if (::bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0) { printf("bind port %d failed!n", port); return false; } printf("bind port %d success!n", port); listen(sock, 10); //这个函数已经调用,就会告诉系统,这个socket可以通过这 //个端口来跟它进行接收连接了 //但是,连接的信息还会用到另外一个函数。10指的是在我这次获取连接信息的时候 //最大支持多少个连接信息 return true; } int XTcp::Recv(char* buf, int bufsize) { //Revc考虑一点,有时候需要接收指定大小,接收不到指定大小,就会失败 return recv(sock, buf, bufsize, 0); } int XTcp::Send(const char* buf, int size) { int s = 0; while(s!=size) //一直发送,直到发送完毕 { int len = send(sock, buf + s, size - s, 0); if (len <= 0) break; s += len; } return s; } void XTcp::Close() { if (sock <= 0) return; closesocket(sock); } XTcp XTcp::Accept() { XTcp tcp; sockaddr_in caddr; socklen_t len = sizeof(caddr); int client = accept(sock, (sockaddr*)&caddr, &len);//创建一个新的socket,用来与客户端单独进行通信 if (client <= 0) return tcp; //如何让外部知道我accept是成功还是失败,可以通过这个返回对象的默认socket来判断 printf("accept client %dn", client); tcp.ip = inet_ntoa(caddr.sin_addr); tcp.port = ntohs(caddr.sin_port); //将网络字节序转化为本地字节序 tcp.sock = client; printf("client ip is %s,port is %dn", tcp.ip.c_str(), tcp.port); return tcp; } XTcp::~XTcp() { }
XTcp.h
//#pragma once #ifndef _XTCP_H_ #define _XTPC_H_ #includeclass XTcp { public: int CreateSocket(); bool Bind(unsigned short port); XTcp Accept(); //注意这个返回类型不是指针类型,我们需要在后面重新调用一个closesocket函数 void Close(); int Recv(char *buf,int bufsize); int Send(const char *buf,int sendsize); XTcp(); virtual ~XTcp(); int sock = 0; unsigned short port = 0; std::string ip; //这些信息可以对外开放 }; #endif //_XTCH_H_
如上面的内容。我们完成了动态库的生成,下面需要进行动态库的调用
如下服务器代码:server.cpp
#include#include #include #include"XTcp.h" class TcpThread { public: void Main() { char buffer[1024] = { 0 }; for (;;) { int recvlen = client.Recv(buffer, sizeof(buffer) - 1); if (recvlen <= 0) break; buffer[recvlen] = ''; if (strstr(buffer, "quit") != NULL) { char re[] = "quit success!n"; client.Send(re, strlen(re) + 1); break; } int sendlen = client.Send("okn", 4); printf("recv %sn", buffer); } client.Close(); delete this; } XTcp client; }; int main(int argc, char* argv[]) { unsigned short port = 8080; if (argc > 1) { port = atoi(argv[1]); } XTcp server; server.CreateSocket(); server.Bind(port); for (;;) { XTcp client = server.Accept(); TcpThread* th = new TcpThread; //需要考虑什么时候清理这个线程对象 th->client = client; std::thread sth(&TcpThread::Main, th); //第二个参数是对象,第一个参数是对象的方法 sth.detach(); //表示释放主线程拥有的子线程的资源 } //来支持多个客户端 server.Close(); getchar(); return 0; }
看下面的编译与链接xsocket动态库的makefile
tcpserver:server.cpp g++ $+ -o $@ -I../xsocket/xsocket -std=c++11 -lpthread -lxsocket -L../xsocket/xsocket
这样就完成了linux下动态库的生成与调用,但是还有一个问题,我们生成的动态库需要配置下环境变量之后,我们才能正常调用这个动态库.
可看如下代码,提供了一种解决方法
export LD_LIBRARY_PATH=../xsocket/xsocket ./tcpserver
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)