C/C++ TCP/IP通信函数
这里提供了两个C/C++中服务器与客户端之间通讯的两个程序,程序中封装了通信之间的函数方法,我们以这个程序为例进行封装。
文件目录结构按照C/C++标准开源项目进行存放:
├─bin ├─doc ├─lib └─src ├─xsocket │ │ XTCP.h │ │ XTCP.cpp
XTCP.h
#ifndef XTCP_H //保证只初始化一次 #define XTCP_H #ifdef WIN32 #pragma once #ifdef XSOCKET_EXPORTS #define XSOCKET_API __declspec(dllexport) #else #define XSOCKET_API __declspec(dllimport) #endif #else #define XSOCKET_API #endif // WIN32 #include<string> class XSOCKET_API XTCP { public: int createSocket(); bool bindListen(unsigned short port); void closeSocket(); int receiveData(char* buf,int bufsize); int sendData(const char* buf, int sendsize); bool connectSocket(const char* ip, unsigned short port); XTCP acceptClient(); XTCP(); virtual ~XTCP(); int sock = 0; unsigned short port = 0; char ip[16]; }; #endif // !XTCP_H
XTCP.cpp
#include "XTCP.h" #ifdef WIN32 #include<Windows.h> #define socklen_t int #else #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<arpa/inet.h> //函数名替换,重定义,将close关闭sock的函数转化为closesocket #define closesocket close #define strcpy_s strcpy #endif #include <iostream> #include<stdlib.h> #include<cstring> XTCP::XTCP() { #ifdef WIN32 static bool is_first = true; if (is_first) { is_first = false; //通过进程启动Winsock DLL使用 WSADATA ws; WSAStartup(MAKEWORD(2, 2), &ws); } #endif } bool XTCP::connectSocket(const char* ip, unsigned short port) { if (sock <= 0) { createSocket(); } sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(port); //将字符串ip地址转化为网络地址 saddr.sin_addr.s_addr = inet_addr(ip); if (connect(sock, (const sockaddr*)&saddr, sizeof(saddr)) != 0) { // strerror(errno)将错误转化为字符串 std::cout << "connect " << ip << " : " << port << " failed! "; return false; } std::cout << "connect " << ip << " : " << port << " success!\n "; return true; } int XTCP::createSocket() { //创建socket,创建失败返回-1,AF_INET表示ipv4协议,SOCK_STREAM表示接受tcp/ip协议的数据 sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { std::cout << "create socket failed" << std::endl; } return sock; } bool XTCP::bindListen(unsigned short port) { if (sock <= 0) { createSocket(); } //绑定地址 sockaddr_in saddr; saddr.sin_family = AF_INET; //大端字节序和小端字节序的问题, saddr.sin_port = htons(port); //0设置为绑定本机地址 saddr.sin_addr.s_addr = htonl(0); //绑定地址 if (bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0) { std::cout << "bind port " << port << " failed.\n"; return false; } std::cout << "bind port " << port << " successful.\n"; //监听客户端发送的信息 //backlog=10表示缓冲大小 listen(sock, 10); return true; } XTCP XTCP::acceptClient() { XTCP tcp; //每个连接就会生成一个client sockaddr_in caddr; socklen_t len = sizeof(caddr); //在accept之前会进行三次握手(由操作系统完成),accept只是获取了握手后的信息 int client_sock = accept(sock, (sockaddr*)&caddr, &len); if (client_sock <= 0) { return tcp; } tcp.sock = client_sock; std::cout << "accept client " << client_sock << ".\n"; char* ip = inet_ntoa(caddr.sin_addr); strcpy_s(tcp.ip, ip); tcp.port = ntohs(caddr.sin_port); std::cout << "client ip address " << tcp.ip << ".\n"; std::cout << "client port " << tcp.port << ".\n"; return tcp; } int XTCP::receiveData(char* buf, int bufsize) { return recv(sock, buf, bufsize, 0); } int XTCP::sendData(const char* buf, int sendsize) { //需要全部发送完全才能结束 int sendedSize = 0; while (sendedSize!=sendsize) { int len = send(sock, buf + sendedSize, sendsize - sendedSize, 0); if (len <= 0) { break; } sendedSize += len; } return sendedSize; } void XTCP::closeSocket() { if (sock <= 0) { return; } closesocket(sock); } XTCP::~XTCP() { }
Linux封装.so文件
首先编写编写makefile文件,用于编译两个程序,可以按照以下命令进行编译,编译后将会在同级目录下生成一个libxsokcet.so文件,理论上这个时候Linux封装就完成了。
libxsocket.so:XTCP.cpp XTCP.h g++ $+ -o $@ -fpic -shared -std=c++11
但是在实际过程中,常常会出现一个.so文件不存在/找不到的问题。我们在这里编写一个测试程序,项目创建在和xsocket的同级目录,测试libxsocket.so是否可以调用。
testTCP.cpp
#include <iostream> #include"XTCP.h" int main() { XTCP client; client.connectSocket("192.168.245.129", 8080); return 0; }
我们通过以下程序编译这个程序之后:
testTCP:testTCP.cpp g++ $+ -o $@ -I../xsocket -std=c++11 -lpthread -lxsocket -L../xsocket
可以发现,尽管我们在编译过程中设置了libxsocket.so的路径所在位置,但是在运行之后还是会存在找不到文件,报错内容如下error while loading shared libraries: libxsocket.so: cannot open shared object file: No such file or directory,所以在运行的时候需要重新编写一个脚本,来执行testTCP,并且显示指定libxsocket.so的路径所在位置,脚本如下所示:
export LD_LIBRARY_PATH=../xsocket ./testTCP
这样testTCP就执行成功了。
Windows封装.lib文件
Windows封装.lib文件,我们借助vs studio 2019编译器完成编译,还是以上述两个文件和文件结构为例:
首先在xsocket的同级目录创建一个项目(选择具有导出项的(DLL)动态链接库,千万不要选错了),项目名自取。
项目创建后将会自动生成一些配置文件,如下图所示
然后将XTCP.h和XTCP.cpp文件添加到目前这个新建的项目中来,文件结构如上图所示,具体简体方法,
1.XTCP.h和XTCP.cpp文件添加到当前项目目录。
2.选择 头文件 -> 添加 -> 现有项 ,添加XTCP.h文件。
3.XTCP.cpp文件和XTCP.h文件添加方法一致。
添加文件后,需要设定一系列的项目配置项:
1、选择项目,右键,选择属性
修改输出目录
修改工作目录
取消预编译头
修改导出库目录
然后点击运行就会在bin目录生成和项目同名的dll文件了。
中间可能会有一个弹窗显示错误,可以忽略,只要编译显示没有失败/错误就行。
验证是否可以使用,在项目目录新建一个项目,用于验证该dll是否可以使用:
在新建项目的 解决方案上右键,选择添加现有项目,将刚才打包的项目添加进行,选择.vcxproj文件。
然后再右键 解决方案 ,设置启动项目和项目依赖项,
按照下图设置,设置新建项目为主项目,依赖项目为打包项目。
然后再设置当前项目的动态链接库位置,选择当前项目,右键,选择属性,如下图所示:
然后再在当前项目新建一个cpp文件运行以下程序,
#include <iostream> #include"XTCP.h" int main() { XTCP client; client.connectSocket("192.168.245.129", 8080); return 0; }
如果不报错则,动态链接库封装成功。