基於tcp協議的socket網絡編程總結

什麼是套接字

套接字是一個主機本地應用程序所創建的,爲操作系統所控制的接口。
應用進程通過這個接口,使用傳輸層提供的服務,跨網絡發送/接收消息到其他應用進程。
Client/Server模式的通信接口——套接字接口

套接字:描述符

OS將文件描述符實現爲一個指針數組,指向一個內部的數據結構:進程描述符表的下標
套接字和文件類似,每個活動套接字使用一個小整數標識,進程的文件描述符和套接字描述符值不能相同
socket函數:創建套接字描述符(不是open函數)

進程的文件描述符表
在這裏插入圖片描述

socket

int socket(int domain,int type,int protocol)
功能:創建一個新的套接字,返回套接字描述符
參數說明:
domain:域類型,指明使用的協議棧,如TCP/IP使用的是PF_INET
type:指明需要的服務類型,如
SOCK_DGRAM:數據報服務,UDP協議
SOCK_STREAM:流服務,TCP協議
protocol:一般都取0
舉例:s = socket(PF_INET,SOCK_STREAM,0)

connect

int connect(int sockfd,struct sockaddr *server_addr,int sockaddr_len)
功能:同遠程服務器建立主動連接,成功時返回0,若連接失敗返回-1
參數說明:
Sockfd:套接字描述符,指明創建連接的套接字
Server_addr:指明遠程端點:IP地址和端口號
sockaddr_len:地址長度
舉例(P49):connect(s,remaddr,remaddrlen)

send

int send(int sockfd,const void * data,int data_len,unsigned int flags)
功能:
在TCP連接上發送數據,返回成功傳送數據的長度,出錯時返回-1
send會將外發數據複製到OS內核中,也可以使用send發送面向連接的UDP報文
參數說明:
sockfd:套接字描述符
data:指向要發送數據的指針
data_len:數據長度
flags:一直爲0

recv

int recv(int sockfd,void *buf,int buf_len,unsigned int flags);
功能:
從TCP接收數據,返回實際接收的數據長度,出錯時返回-1
服務器使用其接收客戶請求,客戶使用它接受服務器的應答。如果沒有數據,將阻塞,如果收到的數據大於緩存的大小,多餘的數據將丟棄。也可以使用recv接收面向連接的UDP的報文
參數說明:
Sockfd:套接字描述符
Buf:指向內存塊的指針
Buf_len:內存塊大小,以字節爲單位
flags:一般爲0
舉例:recv(sockfd,buf,8192,0)

close

close(int sockfd);
功能:
撤銷套接字
如果只有一個進程使用,立即終止連接並撤銷套接字,如果多個進程共享該套接字,將引用數減一,如果引用數降到零,則撤銷它。
參數說明:
Sockfd:套接字描述符
舉例:close(socket_descriptor)

bin

int bin(int sockfd,struct sockaddr *my_addr,int addrlen)
功能:爲套接字指明一個本地端點地址
TCP/IP協議使用sockaddr_in結構,包含IP地址和端口號
服務器使用它來指明熟知的端口號,然後等待連接

參數說明:
Sockfd:套接字描述符,指明創建連接的套接字
my_addr:本地地址,IP地址和端口號
addrlen:地址長度
舉例:bin(sockfd,(struct sockaddr*)&address,sizeof(address));

listen

int listen(int sockfd,int input_queue_size)
功能:
面向連接的服務器使用它將一個套接字置爲被動模式,並準備接收傳入連接。用於服務器,指明某個套接字連接是被動的。
參數說明:
Sockfd:套接字描述符,指明創建連接的套接字
input_queue_size:該套接字使用的隊列長度,指定在請求隊列中允許的最大請求數
舉例:listen(sockfd,20)

accept

int accept(int sockfd,void *addr,int *addrlen);
int accept(int sockfd,struct sockaddr *addr,int *addrlen);
功能:獲取傳入連接請求,返回新的連接的套接字描述符。
爲每個新的連接請求創建了一個新的套接字,服務器只對新的連接使用該套接字,原來的監聽套接字接受其他的連接請求。
新的連接上傳輸數據使用新的套接字,使用完畢,服務器將關閉這個套接字。

參數說明:
Sockfd:套接字描述符,指明正在監聽的套接字
addr:提出連接請求的主機地址
addrlen:地址長度
舉例:
new_sockfd = accept(sockfd,(struct sockaddr*)&address,sizeof(address));

用於整數轉換的實用例程
網絡字節順序:最高位字節在前
有結套接字例程要求參數按照網絡字節順序存儲。如sockaddr_in
需要網絡字節順序和本地主機字節順序進行轉換的函數,堅持使用,便於移植。
分爲短(short 16位)和長(long 32位)兩種
htons:將一個短整數從本地字節順序轉換爲網絡字節順序;
ntohs:將一個短整數從網絡字節順序轉換爲本地字節順序;
htonl和ntohl:類似如上
在這裏插入圖片描述

套接字API中的主要系統調用
read和write
在UNIX和Linux中,可以代替recv和send,因爲都調用內核的sosend實現。

在這裏插入圖片描述

Linux

TCP服務端

Ubuntu 16.04.6 LTS

mkdir tcpserver 
cd tcpserver
vim tcpserver.cpp
#include <iostream>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
using namespace std;
//#ifndef errno
//extern int errno;
//#endif
int main(int argc,char *argv[])
{
        unsigned short port = 8080;
        if(argc>1)
        {
                port = atoi(argv[1]);
        }

        //1 create socket
        int sock = socket(AF_INET,SOCK_STREAM,0);
        if(sock<=0)
        {
                cerr<<"cretor socket error "<<strerror(errno)<<endl;
                return -1;
        }
        //2 bind port
        sockaddr_in saddr;
        memset(&saddr,0,sizeof(saddr));
        saddr.sin_family = AF_INET;
        saddr.sin_port = htons(port);
        saddr.sin_addr.s_addr = htonl(0);
        int re = ::bind(sock,(sockaddr*)&saddr,sizeof(saddr));
        if(re != 0)
        {
                cerr<<"bind port "<<port<<" failed!"<<strerror(errno)<<endl;
                return -1;
        }
        cout<<"bind port "<<port<<" success!"<<endl;

        //3 listen
        listen(sock,10);
        //4 accept
        {
        sockaddr_in caddr;
        socklen_t addrlen = 0;
        int client_sock = accept(sock,(sockaddr*)&caddr,&addrlen);
        cout<<"client sock = "<<client_sock<<endl;

        // send
        char buf[1024] = "wellcome to xms";
        int len = send(client_sock,buf,strlen(buf),0);
        cout<<"send len = "<<len<<endl;

        // recv
        len = recv(client_sock,buf,sizeof(buf)-1,0);
        if(len>0)
        {
                buf[len] = '\0';
                cout<<buf<<endl;
        }
        }
        return 0;
}

Ubuntu 18.x

#include <iostream>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
using namespace std;
int main(int argc,char *argv[])
{
	unsigned short port = 8080;
	if(argc>1)
	{
		port = atoi(argv[1]);
	}

	//1 create socket
	int sock = socket(AF_INET,SOCK_STREAM,0);
	if(sock<=0)
	{
		cerr<<"cretor socket error "<<strerror(errno)<<endl;
		return -1;
	}
	//2 bind port
	sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	saddr.sin_addr.s_addr = htonl(0);
	int re = ::bind(sock,(sockaddr*)&saddr,sizeof(saddr));
	if(re != 0)
	{
		cerr<<"bind port "<<port<<" failed!"<<strerror(errno)<<endl;
		return -1;
	}
	cout<<"bind port "<<port<<" success!"<<endl;

	//3 listen
	listen(sock,10);
	//4 accept
	{
	sockaddr_in caddr;
	socklen_t addrlen = 0;
	int client_sock = accept(sock,(sockaddr*)&caddr,&addrlen);
	cout<<"client sock = "<<client_sock<<endl;

	// send
	char buf[1024] = "wellcome to xms";
	int len = send(client_sock,buf,strlen(buf),0);
	cout<<"send len = "<<len<<endl;

	// recv
	len = recv(client_sock,buf,sizeof(buf)-1,0);
	if(len>0)
	{
		buf[len] = '\0';
		cout<<buf<<endl;
	}
	}
	return 0;
}

vim makefile
TARGET=tcpserver
OBJS=tcpserver.o
$(TARGET):$(OBJS)
		g++ $+ -o $@
clean:
		rm -rf $(TARGET)
		rm -rf $(OBJS)
make

TCP客戶端

mkdirr tcpclient
touch tcpclient.cpp
cp tcpserver/makefile tcpclient
cd tcpclient
vim tcpclient.cpp
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
using namespace std;
int main(int argc,char *argv[])
{
	unsigned short port = 8080;
	const char *ip = "127.0.0.1";
	//tcpclient 192.168.0.205 8080
	if(argc>2)
	{
		ip = argv[1];
		port = atoi(argv[2]);
	}
	int sock = socket(AF_INET,SOCK_STREAM,0);
	sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	saddr.sin_addr.s_addr = inet_addr(ip);
	int re = connect(sock,(sockaddr*)&saddr,sizeof(saddr));
	if(re != 0)
	{
		cerr<<"connect "<<ip<<":"<<port<<" failed!"<<strerror(errno)<<endl;
		return -1;
	}
	cout<<"connect "<<ip<<":"<<port<<" success!"<<endl;
	char buf[1024] = {0};
	int len = recv(sock,buf,sizeof(buf)-1,0);
	if(len>0)
	{
		cout<<buf<<endl;
	}
	strcpy(buf,"send from client");
	len = send(sock,buf,strlen(buf),0);
	close(sock);

	return 0;
}

makefile

TARGET=tcpclient
OBJS=tcpclient.o
$(TARGET):$(OBJS)
	g++ $+ -o $@ -std=c++11
clean:
	rm -rf $(TARGET)
	rm -rf $(OBJS)	

Windows

TCP客戶端

#include <iostream>
#include <sys/types.h>
#ifdef _WIN32
#include <windows.h>

#define close closesocket
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif
#include <string.h>
using namespace std;
int main(int argc,char *argv[])
{
    //windows初始化socket庫
#ifdef _WIN32
    WSADATA ws;
    WSAStartup(MAKEWORD(2, 2), &ws);
#endif
	unsigned short port = 8080;
	const char *ip = "127.0.0.1";
	//tcpclient 192.168.0.205 8080
	if(argc>2)
	{
		ip = argv[1];
		port = atoi(argv[2]);
	}
	int sock = socket(AF_INET,SOCK_STREAM,0);
    if (sock < 0)
    {
        cerr << "socket failed!" << strerror(errno) << endl;
        return -1;
    }
	sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(port);
	saddr.sin_addr.s_addr = inet_addr(ip);
	int re = connect(sock,(sockaddr*)&saddr,sizeof(saddr));
	if(re != 0)
	{
    	cerr<<"connect "<<ip<<":"<<port<<" failed!"<<strerror(errno)<<endl;
		return -1;
	}
	cout<<"connect "<<ip<<":"<<port<<" success!"<<endl;
	char buf[1024] = {0};
	int len = recv(sock,buf,sizeof(buf)-1,0);
	if(len>0)
	{
		cout<<buf<<endl;
	}
	strcpy(buf,"send from client");
	len = send(sock,buf,strlen(buf),0);
	close(sock);

	return 0;
}

添加_CRT_SECURE_NO_WARNINGS
在這裏插入圖片描述
在這裏插入圖片描述

添加ws2_32.lib
在這裏插入圖片描述
在這裏插入圖片描述
輸入參數IP+端口
在這裏插入圖片描述
linux server

./tcpserver &

windows client

./tcpclient

libevent和IO模型的學習總結

同步異步和阻塞

socket,阻塞,blocking,結果返回前,當前線程被掛起
epool select,非阻塞,nonblocking,結果返回之前,線程不阻塞
Sync,同步,功能調用無結果不返回,事件一件件做
Async,異步,功能調用後無結果立刻返回,等待通知,一件事沒完成就可以做下一件——IOCP

IO模型

BIO,阻塞IO,Blocking IO,傳統的socket編程方式
NIO,同步非阻塞IO,NonBlocking IO,主動詢問是否就緒,select循環遍歷檢測
AIO,異步非阻塞IO,採用proactor模式,數據完成後,由OS主動通知應用程序IOCP

Reactor模式

event-driven architecture——事件驅動體系結構

reactor設計模式是event-driven architecture的一種實現方式

epoll基於事件驅動思想,採用reactor模式
事件分發器——事件分發器的兩種模式,1Reactor,2Proactor

處理者(event handler)

說明

反應器設計模式(Reactor pattern)是一種爲處理併發服務請求,並將請求提交到一個或者多個服務處理程序的事件設計模式。

當客戶端請求抵達後,服務處理程序使用多路分配策略,由一個非阻塞的線程來接收所有的請求,然後派發這些請求至相關的工作線程進行處理。

簡單說就是如何處理多個客戶端的併發請求的解決模式。

首先Reactor模式中可定義三種角色:
1、Reactor負責監聽和分配事件,將I/O事件分派給對應的Handler
2、Acceptor處理客戶端新連接,並分派請求到處理器鏈中
3、Handlers執行非阻塞讀/寫任務,可用資源池來管理
4、Epoll本質來講是同步非阻塞

Proactor模式
IOCP本質上來講則是異步操作

Windows平臺編譯libevent

  1. 環境準備
    Windows 10 64位
    VS2019企業版
    perl 編譯openssl用
    nasm
    zlib 1.2.11源碼,http://zlib.net/
    openssl 1.1.1源碼,https://www.openssl.org/source/
    libevent 2.1.8源碼,

  2. 編譯zlib
    在這裏插入圖片描述

解壓zlib-1.2.11.tar.gz到當前目錄

打開x64_x86 Cross Tools Command Prompt for VS 2019

手動編譯執行

nmake /f WIN32\Makefile.msc

在這裏插入圖片描述
批處理腳本編譯,build_zlib_vs2019_32.bat

set VS="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
set OUT = "D:\libevent\out\vs2019_32\zlib"
call %VS%
cd zlib-1.2.11
nmake /f win32\Makefile.msc clean
nmake /f win32\Makefile.msc
md %OUT%\lib
md %OUT%\bin
md %OUT%\include
copy /Y *.lib %OUT%\lib
copy /Y *.h %OUT%\include
copy /Y *.dll %OUT%\bin
copy /Y *.exe %OUT%\bin
pause

上面的bat腳本代碼在不同版本下有可能會報錯,因爲權限的問題,換成下面絕對路徑的寫法

set VS = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
set OUT = "D:\libevent\out\vs2019_32\zlib"
call %VS%
cd zlib-1.2.11
nmake /f win32\Makefile.msc clean
nmake /f win32\Makefile.msc
md D:\libevent\out\vs2019_32\zlib\lib
md D:\libevent\out\vs2019_32\zlib\bin
md D:\libevent\out\vs2019_32\zlib\include
copy /Y *.lib D:\libevent\out\vs2019_32\zlib\lib
copy /Y *.h D:\libevent\out\vs2019_32\zlib\include
copy /Y *.dll D:\libevent\out\vs2019_32\zlib\bin
copy /Y *.exe D:\libevent\out\vs2019_32\zlib\bin
pause

以管理員身份運行
在這裏插入圖片描述
在這裏插入圖片描述

  1. 編譯openssl

安裝 nasm 彙編器,設置PATH環境變量,添加D:\libevent\nasm-2.13.03-win64\nasm-2.13.03
安裝perl,默認安裝即可
手動編譯openssl
打開x64_x86 Cross Tools Command Prompt for VS 2019

set OUTPATH = "D:\libevent\out\vs2019_32\openssl"
perl Configure {VC-WIN32|VC-WIN64|VC-CE} --prefix=%OUTPATH%

批處理腳本編譯

@echo "start compile openssl"
set VS = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
set OUT = "D:\libevent\out\vs2019_32\openssl"
call %VS%
D:
cd D:\libevent\openssl-1.1.1g
perl Configure VC-WIN32 --prefix=D:\libevent\out\vs2019_32\openssl
nmake clean
nmake
nmake install
@echo "compile end"
pause

perl Configure VC-WIN32 --prefix=%OUT%修改成這樣無法正確執行,可能是權限問題,所以建議還是加上絕對路徑。

  1. 編譯libevent
    手動編譯
nmake /f Makefile.nmake OPENSSL_DIR=D:\libevent\out\vs2019_32\openssl

編譯到最後會提示錯誤
LINK : fatal error LNK1181: 無法打開輸入文件 openssl\lib\libeay32.lib

cd D:\libevent\libevent-master\test
修改Makefile.nmake
將SSL_LIBS的libeay32.lib和ssleay32.lib這兩個文件替換成D:\libevent\out\vs2019_32\openssl\lib目錄下的libcrypto.lib和libssl.lib

SSL_LIBS=..\libevent_openssl.lib $(OPENSSL_DIR)\lib\libeay32.lib $(OPENSSL_DIR)\lib\ssleay32.lib gdi32.lib User32.lib

修改爲

SSL_LIBS=..\libevent_openssl.lib $(OPENSSL_DIR)\lib\libcrypto.lib $(OPENSSL_DIR)\lib\libssl.lib gdi32.lib User32.lib

保存再重新手動執行

nmake /f Makefile.nmake OPENSSL_DIR=D:\libevent\out\vs2019_32\openssl clean
nmake /f Makefile.nmake OPENSSL_DIR=D:\libevent\out\vs2019_32\openssl

直到出現這樣的畫面就可以了。在這裏插入圖片描述
然後檢測一下是否安裝成功,打開目錄D:\libevent\libevent-master\test
運行regress.exe,若仍然提示libcrypto-1_1.dll和libssl-1_1.dll缺失,可以將D:\libevent\out\vs2019_32\openssl\bin目錄下的libcrypto-1_1.dll和libssl-1_1.dll拷貝到D:\libevent\libevent-master\test目錄下,再重新執行regress.exe > …/…/out.txt

發現zlib沒有測試通過,如何解決?
在這裏插入圖片描述
把之前編譯好的zlib整個目錄拷貝到D:\libevent\libevent-master目錄下,並修改Makefile.nmake的CFLAGS,添加/I…/zlib/include
在這裏插入圖片描述
添加regress_zlib.obj
在這裏插入圖片描述
修改D:\libevent\libevent-master\WIN32-Code\nmake\event2\event-config.h
在這裏插入圖片描述

先清除

nmake /f Makefile.nmake OPENSSL_DIR=D:\libevent\out\vs2019_32\openssl clean

再重新編譯

cd D:\libevent\libevent-master
nmake /f Makefile.nmake 

最後編譯通過
在這裏插入圖片描述
測試一下zlib例子

cd D:\libevent\libevent-master\test
regress.exe

在這裏插入圖片描述
把zlib1.dll文件拷貝到D:\libevent\libevent-master\test目錄下
在這裏插入圖片描述

D:\libevent\libevent-master\test>regress.exe > ../../out.txt

bufferevent_zlib測試成功了
在這裏插入圖片描述
build_libevent_vs2019_32.bat

@echo "start compile libevent"
set VS = "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
set OUT = D:\libevent\out\vs2019_32\libevent
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
cd /d D:\libevent\libevent-master
nmake /f Makefile.nmake clean
nmake /f Makefile.nmake OPENSSL_DIR=D:\libevent\out\vs2019_32\openssl
md ..\out\vs2019_32\libevent\lib
md ..\out\vs2019_32\libevent\bin
md ..\out\vs2019_32\libevent\include
copy /Y *.lib ..\out\vs2019_32\libevent\lib
xcopy /S/Y include ..\out\vs2019_32\libevent\include
xcopy /S/Y WIN32-Code\nmake ..\out\vs2019_32\libevent\include
copy /Y *.dll ..\out\vs2019_32\libevent\bin
copy /Y *.exe ..\out\vs2019_32\libevent\bin

@echo "compile end"
pause
  1. 編寫第一個libevent測試程序

visual studio 2019創建控制檯C++應用程序,勾選“解決方案和項目放在同一目錄”,工程項目保存到src目錄下
工程目錄結構如下:
在這裏插入圖片描述在這裏插入圖片描述
項目工程右鍵屬性,分別修改以下參數
(1)c/c++===> 預編譯頭 =>不使用預編譯頭
(2)c/c++
=> 常規 ===> 附加包含目錄,添加…\include
(3)鏈接器 ===> 常規 ===> 附加庫目錄,添加…\lib
(4)鏈接器 ===> 輸入 ===> 附加依賴項,添加libevent.lib;ws2_32.lib
(5)鏈接器 ===> 命令行 ,添加/NODEFAULTLIB:“libcmtd.lib”

修改main.cpp

#include <iostream>
#include <event2/event.h>
using namespace std;
int main()
{
#ifdef _WIN32
    //初始化socket庫,windows平臺下一定要初始化socket庫,而linux平臺不用
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2), &wsa);
#endif
    std::cout << "test libevent!\n";
    event_base* base = event_base_new();
    if (base)
    {
        cout << "event_base_new success!" << endl;
    }
    system("pause");
    return 0;
}

執行成功

test libevent!
event_base_new success!

Linux平臺編譯libevent

ubuntu18.04.01 64

apt-get install perl g++ make automake libtool unzip

zlib

tar -xvf zlib-1.2.11.tar.gz
cd zlib-1.2.11
./configure
make -j6
sudo make install

可以看到庫文件都拷貝到/usr/local/lib目錄下,頭文件在/usr/local/include目錄,在這些目錄的文件,你的程序可以不用指定庫文件路徑,頭文件也是一樣。
在這裏插入圖片描述

openssl-1.1.1

tar -xvf openssl-1.1.1.tar.gz
./config
make -j6
sudo make install

同樣在安裝的時候也是把這些生成的文件拷貝到這些目錄下
/usr/local/lib/
/usr/local/include
……
你的程序可以不用指定庫文件路徑,頭文件也是一樣。

libevent
在安裝libevent之前,確保你的linux系統已經安裝automake和libtool

unzip libevent-master.zip
cd libevent-master
./autogen.sh 生成configure
./configure
make -j6
sudo make install
test/regress >log.txt

zlib測試通過
在這裏插入圖片描述
ssl測試通過
在這裏插入圖片描述
在原來first-libevent項目的基礎上修改一下,直接在linux環境下編譯。

vim makefile
firstlibevent:first_libevent.cpp
				g++ $^ -o $@ -levent
				./$@
clean:
				rm -rf firstlibevent
				rm -rf *.o

按下esc,wq退出

$: make
g++ first_libevent.cpp -o firstlibevent -levent -L /usr/local/lib
./firstlibevent
./firstlibevent: error while loading shared libraries: libevent-2.2.so.1: cannot open shared object file: No such file or directory

linux 缺少動態連接庫.so——cannot open shared object file: No such file or directory

$: whereis libevent-2.2.so
.1
libevent-2.2.so: /usr/local/lib/libevent-2.2.so.1
$: ldd /usr/local/lib/libevent-2.2.so.1
        linux-vdso.so.1 =>  (0x00007fff8638f000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3e29e84000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3e29aba000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3e2a2f8000)

libevent-2.2.so.1軟鏈接的路徑是正常存在的,出現這種情況,有可能是$LD_LIBRARY_PATH環境變量設置的問題

(caffe2_env) zhoujianwen@zhoujianwen-System:/media/zhoujianwen/Data/libevent/src/first_libevent$ echo $LD_LIBRARY_PATH
/usr/local/cuda-10.0/lib64

在makefile頭部添加
LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
注意:它只對當次make執行操作有效,make之後再單獨執行./firstlibevent仍然會提示找不到共享庫xxx.so,要永久性添加就要將該 LD_LIBRARY_PATH 的 export 語句寫到系統文件中,結尾會提及到。

vim makefile
LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
firstlibevent:first_libevent.cpp
		g++ $^ -o $@ -levent
		./$@
clean:
		rm -rf firstlibevent
		rm -rf *.o
(caffe2_env) zhoujianwen@zhoujianwen-System:/media/zhoujianwen/Data/libevent/src/first_libevent$ make clean
rm -rf firstlibevent
rm -rf *.o
(caffe2_env) zhoujianwen@zhoujianwen-System:/media/zhoujianwen/Data/libevent/src/first_libevent$ make
g++ first_libevent.cpp -o firstlibevent -levent
./firstlibevent
test libevent!
event_base_new success!

linux中每次裝完一個新的庫,需要進行ldconfig命令

sudo ldconfig

https://www.pianshen.com/article/8867176161/
https://blog.csdn.net/qq_33343767/article/details/90378535

爲什麼修改LD_LIBRARY_PATH呢

因爲運行時動態庫的搜索路徑的先後順序是:
1.編譯目標代碼時指定的動態庫搜索路徑;
2.環境變量LD_LIBRARY_PATH指定的動態庫搜索路徑;
3.配置文件/etc/ld.so.conf中指定的動態庫搜索路徑;
4.默認的動態庫搜索路徑/lib和/usr/lib;

這個順序是compile gcc時寫在程序內的,通常軟件源代碼自帶的動態庫不會太多,而我們的/lib和/usr/lib只有root權限纔可以修改,而且配置文件/etc/ld.so.conf也是root的事情,我們只好對LD_LIBRARY_PATH進行操作啦。

永久性添加

每次我使用該軟件都需要臨時修改庫文件,因爲上面的方法是臨時設置環境變量 LD_LIBRARY_PATH ,重啓或打開新的 Shell 之後,一切設置將不復存在。

爲了讓這種方法更完美一些,可以將該 LD_LIBRARY_PATH 的 export 語句寫到系統文件中,例如 /etc/profile、/etc/export、~/.bashrc 或者 ~/.bash_profile 等等,取決於你正在使用的操作系統咯。
修改完系統文件記得source ~/.bashrc纔會生效。

基於event監控客戶端接收連接

event註冊服務端接收連接事件
(1) 創建event_base上下文
(2) 創建socket綁定端口
(3) 註冊socket監聽事件的回調函數
(4) 接收客戶端連接
搭建服務器接收用戶連接——evconnlistener_new_bind
使用bufferevent連接服務器
基於bufferevent數據通信

服務器端幾種模型:

1、阻塞式模型(blocking IO)
2、多線程的服務器模型(Multi-Thread)
3、非阻塞式模型(Non-blocking IO)
4、多路複用IO
5、使用事件驅動庫libevent的服務器模型
6、信號驅動IO模型(Signal-driven IO)
7、異步IO模型(asynchronous IO)
幾種服務器端IO模型的簡單介紹及實現 http://www.cnblogs.com/luxiaoxun/p/3691800.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章