开源框架专题
四、开源框架专题
0.项目仓库
1.字符编码和压缩
1.1 字符编码
-
ASCII(American Standard Code for Information Interchange),128个字符,用7位二进制表示(00000000-01111111即0x00-0x7F);EASCII(Extended ASCII),256个字符,用8位二进制表示(00000000-11111111即0x00-0xFF)。当计算机传到了欧洲,国际标准化组织在ASCII的基础上进行了扩展,形成了ISO-8859标准,跟EASCII类似,兼容ASCII,在高128个码位上有所区别。但是由于欧洲的语言环境十分复杂,所以根据各地区的语言又形成了很多子标准,ISO-8859-1、ISO-8859- 2、ISO-8859-3、……、ISO-8859-16。
-
双字节编码可以是变长的,也就是说同一个编码里面有些字符是单字节表示,有些字符是双字节表示。这样做的好处是,一方面可以兼容ASCII,另一方面可以节省存储容量,代价就是会损失一部分码位。
- GBK(Chinese Internal Code Specification汉字内码扩展规范)是GB2312的扩展(gbk编码能够用来同时表示繁体字和简体字),按理说都属于双字节编码,码位是一样的,根本谈不上扩展,但实际上是预留空间在起作用。
- UNICODE字符集(国际标准字符集),它将世界各种语言的每个字符定义一个唯一的编码,以满足跨语言、跨平台的文本信息转换。有多个编码方式,分别是UTF-8,UTF-16,UTF-32编码。
-
UTF是Unicode Transformation Format的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。
-
UFT-8:一种变长的编码方案,使用1~6个字节来存储;
-
UFT-32:一种固定长度的编码方案,不管字符编号大小,直接存储Unicode编号,始终使用4个字节来存储;
-
UTF-16:介于UTF-8和UTF-32之间,使用2个或者4个字节来存储,长度既固定又可变。
-
UTF格式在文件中的固定文件头
-
1.2 libiconv字符编码转换库
-
/* * @Author: gongluck * @Date: 2020-11-18 07:52:34 * @Last Modified by: gongluck * @Last Modified time: 2020-11-18 09:14:11 */ #include <stdio.h> #include <stdlib.h> #include <iconv.h> int main() { iconv_t cd = iconv_open("GBK", "UTF-8"); //UTF8 -> GBK if (cd == (iconv_t)-1) { printf("iconv_open failed.\n"); exit(-1); } char str[] = "iconv例子"; char *pstr = str; size_t len = sizeof(str); char out[100] = {0}; char *pout = out; size_t leftlen = sizeof(out); while (pstr-str < sizeof(str)) { pout = out; leftlen = sizeof(out); int n = iconv(cd, &pstr, &len, &pout, &leftlen); //printf("iconv return %d\n", n); if (pout != out)//有转换成功字符 { printf("%.*s", (int)(sizeof(out)-leftlen), out); } else { break; } if(n != -1) { pout = out; leftlen = sizeof(out); n = iconv(cd, NULL, NULL, &pout, &leftlen); if (pout != out) // 检测iconv内部是否还有缓存 { printf("%.*s", (int)(sizeof(out)-leftlen), out); } break; } } printf("\n"); iconv_close(cd); return 0; }
1.3 zlib
-
编译安装zlib
#下载 wget http://www.zlib.net/zlib-1.2.11.tar.gz #解压 tar -zxvf zlib-1.2.11.tar.gz #进入目录 cd zlib-1.2.11 #配置 ./configure #编译 make -j 8 #检查 make check #安装 sudo make install
-
/* * @Author: gongluck * @Date: 2020-11-18 17:02:24 * @Last Modified by: gongluck * @Last Modified time: 2020-11-18 17:05:37 */ // gcc zlib.c -lz // ./a.out < zlib.c > out // ./a.out -d < out > zlib.c #include <stdio.h> #include <string.h> #include <assert.h> #include "zlib.h" #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) #include <fcntl.h> #include <io.h> #define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) #else #define SET_BINARY_MODE(file) #endif #define CHUNK 1024 /* Compress from file source to file dest until EOF on source. def() returns Z_OK on success, Z_MEM_ERROR if memory could not be allocated for processing, Z_STREAM_ERROR if an invalid compression level is supplied, Z_VERSION_ERROR if the version of zlib.h and the version of the library linked do not match, or Z_ERRNO if there is an error reading or writing the files. */ int def(FILE *source, FILE *dest, int level) { int ret, flush; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; /* allocate deflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit(&strm, level); if (ret != Z_OK) return ret; /* compress until end of file */ do { strm.avail_in = fread(in, 1, CHUNK, source); if (ferror(source)) { (void)deflateEnd(&strm); return Z_ERRNO; } flush = feof(source) ? Z_FINISH : Z_NO_FLUSH; strm.next_in = in; /* run deflate() on input until output buffer not full, finish compression if all of source has been read in */ do { strm.avail_out = CHUNK; strm.next_out = out; ret = deflate(&strm, flush); /* no bad return value */ assert(ret != Z_STREAM_ERROR); /* state not clobbered */ have = CHUNK - strm.avail_out; if (fwrite(out, 1, have, dest) != have || ferror(dest)) { (void)deflateEnd(&strm); return Z_ERRNO; } } while (strm.avail_out == 0); assert(strm.avail_in == 0); /* all input will be used */ /* done when last data in file processed */ } while (flush != Z_FINISH); assert(ret == Z_STREAM_END); /* stream will be complete */ /* clean up and return */ (void)deflateEnd(&strm); return Z_OK; } /* Decompress from file source to file dest until stream ends or EOF. inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be allocated for processing, Z_DATA_ERROR if the deflate data is invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and the version of the library linked do not match, or Z_ERRNO if there is an error reading or writing the files. */ int inf(FILE *source, FILE *dest) { int ret; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; /* allocate inflate state */ strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit(&strm); if (ret != Z_OK) return ret; /* decompress until deflate stream ends or end of file */ do { strm.avail_in = fread(in, 1, CHUNK, source); if (ferror(source)) { (void)inflateEnd(&strm); return Z_ERRNO; } if (strm.avail_in == 0) break; strm.next_in = in; /* run inflate() on input until output buffer not full */ do { strm.avail_out = CHUNK; strm.next_out = out; ret = inflate(&strm, Z_NO_FLUSH); assert(ret != Z_STREAM_ERROR); /* state not clobbered */ switch (ret) { case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */ case Z_DATA_ERROR: case Z_MEM_ERROR: (void)inflateEnd(&strm); return ret; } have = CHUNK - strm.avail_out; if (fwrite(out, 1, have, dest) != have || ferror(dest)) { (void)inflateEnd(&strm); return Z_ERRNO; } } while (strm.avail_out == 0); /* done when inflate() says it's done */ } while (ret != Z_STREAM_END); /* clean up and return */ (void)inflateEnd(&strm); return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; } /* report a zlib or i/o error */ void zerr(int ret) { fputs("zpipe: ", stderr); switch (ret) { case Z_ERRNO: if (ferror(stdin)) fputs("error reading stdin\n", stderr); if (ferror(stdout)) fputs("error writing stdout\n", stderr); break; case Z_STREAM_ERROR: fputs("invalid compression level\n", stderr); break; case Z_DATA_ERROR: fputs("invalid or incomplete deflate data\n", stderr); break; case Z_MEM_ERROR: fputs("out of memory\n", stderr); break; case Z_VERSION_ERROR: fputs("zlib version mismatch!\n", stderr); } } /* compress or decompress from stdin to stdout */ int main(int argc, char **argv) { int ret; /* avoid end-of-line conversions */ SET_BINARY_MODE(stdin); SET_BINARY_MODE(stdout); /* do compression if no arguments */ if (argc == 1) { ret = def(stdin, stdout, Z_DEFAULT_COMPRESSION); if (ret != Z_OK) zerr(ret); return ret; } /* do decompression if -d specified */ else if (argc == 2 && strcmp(argv[1], "-d") == 0) { ret = inf(stdin, stdout); if (ret != Z_OK) zerr(ret); return ret; } /* otherwise, report usage */ else { fputs("zpipe usage: zpipe [-d] < source > dest\n", stderr); return 1; } }
2.Json和Xml
2.1 cJSON
-
/* * @Author: gongluck * @Date: 2020-11-18 21:27:47 * @Last Modified by: gongluck * @Last Modified time: 2020-11-18 21:41:36 */ // gcc *.c -lm #include <stdio.h> #include "cJSON.h" int main() { cJSON* root = cJSON_CreateObject(); cJSON_AddBoolToObject(root, "bool", cJSON_False); cJSON_AddStringToObject(root, "str", "cJSON"); char* str = cJSON_Print(root); printf("%s\n", str); cJSON_Delete(root); root = NULL; root = cJSON_Parse(str); cJSON* b = cJSON_GetObjectItem(root, "bool"); if(b->type == cJSON_True || b->type == cJSON_False) printf("bool value : %d\n", b->type); cJSON* s = cJSON_GetObjectItem(root, "str"); if(s->type == cJSON_String) printf("str value : %s\n", s->valuestring); cJSON_Delete(root); return 0; }
2.2 jsoncpp
-
编译安装
# 下载 git clone https://github.com/open-source-parsers/jsoncpp.git cd jsoncpp mkdir -p build/release cd build/release # 编译 cmake -DCMAKE_BUILD_TYPE=release -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_INCLUDEDIR=include/jsoncpp -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" ../.. make # 安装 sudo make install
-
/* * @Author: gongluck * @Date: 2020-11-19 09:40:21 * @Last Modified by: gongluck * @Last Modified time: 2020-11-19 09:46:42 */ // g++ test.cpp -ljsoncpp #include <iostream> #include <string> #include <jsoncpp/json/json.h> void readJson() { std::string strValue = "{\"name\":\"json\",\"array\":[{\"cpp\":\"jsoncpp\"},{\"java\":\"jsoninjava\"},{\"php\":\"support\"}]}"; Json::Reader reader; Json::Value value; if (reader.parse(strValue, value)) { std::string out = value["name"].asString(); std::cout << out << std::endl; const Json::Value arrayObj = value["array"]; for (unsigned int i = 0; i < arrayObj.size(); i++) { if (!arrayObj[i].isMember("cpp")) continue; out = arrayObj[i]["cpp"].asString(); std::cout << out; if (i != (arrayObj.size() - 1)) std::cout << std::endl; } } } void writeJson() { Json::Value root; Json::Value arrayObj; Json::Value item; item["cpp"] = "jsoncpp"; item["java"] = "jsoninjava"; item["php"] = "support"; arrayObj.append(item); root["name"] = "json"; root["array"] = arrayObj; root.toStyledString(); std::string out = root.toStyledString(); std::cout << out << std::endl; } int main(int argc, char **argv) { readJson(); writeJson(); return 0; }
2.3 TinyXML2
-
安装编译
#下载 tinyxml2 git clone https://github.com/leethomason/tinyxml2.git #进入相应目录并编译 cd tinyxml2/ cmake . make -j 8 #测试该版本的准确性 ./xmltest #安装 sudo make install
-
/* * @Author: gongluck * @Date: 2020-11-19 17:11:24 * @Last Modified by: gongluck * @Last Modified time: 2020-11-19 17:27:23 */ // g++ test.cpp -ltinyxml2 #include <iostream> #include "tinyxml2.h" int main(void) { const char *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> \ <note> \ <to>beijing</to> \ <from>shenzhen</from> \ <heading>Reminder</heading> \ <body>Don't forget the meeting!</body> \ </note>"; tinyxml2::XMLDocument doc; doc.Parse(xml); std::cout << doc.ErrorID() << std::endl; // 1. 第一种刷新到本地 FILE *fp = fopen("memory_1.xml", "wb"); tinyxml2::XMLPrinter printer(fp); doc.Print(&printer); // 打印到文件 fclose(fp); // 2. 第二种刷新到本地 doc.SaveFile("memory_2.xml"); return 0; }
3.ProtoBuf
3.1 序列化和反序列化
-
序列化:把对象转换为字节序列的过程称为对象。
- TLV编码及其变体(tag, length和value的缩写):比如Protobuf。
- 文本流编码:比如XML/JSON。
- 固定结构编码:基本原理是,协议约定了传输字段类型和字段含义,和TLV的方式类似,但是没有了tag和len,只有value,比如TCP/IP。
- 内存dump:把内存中的数据直接输出,不做任何序列化操作。反序列化的时候,直接还原内存。
-
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
3.2 编译安装ProtoBuf
#下载源码
wget https://github.com/protocolbuffers/protobuf/archive/v3.1.0.tar.gz
tar -zxvf v3.1.0.tar.gz
cd protobuf-3.1.0/
#编译
./autogen.sh
./configure
make -j 8
make check
sudo make install
# refresh shared library cache.
sudo ldconfig
3.3 ProtoBuf例子
/*
* @Author: gongluck
* @Date: 2020-11-20 18:16:05
* @Last Modified by: gongluck
* @Last Modified time: 2020-11-20 18:16:05
*/
// protoc --cpp_out=./ addressbook.proto
syntax = "proto3";
package Test;
message Person
{
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType
{
MOBLIE = 0;//首成员必须为0
HOME = 1;
WORK = 2;
}
message PhoneNumber
{
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
}
message AddressBook
{
repeated Person people = 1;
}
/*
* @Author: gongluck
* @Date: 2020-11-20 17:23:22
* @Last Modified by: gongluck
* @Last Modified time: 2020-11-20 18:14:44
*/
// g++ *.cc *.cpp `pkg-config --cflags --libs protobuf`
#include <iostream>
#include "addressbook.pb.h"
using namespace std;
int main()
{
char buf[1024];
int len;
GOOGLE_PROTOBUF_VERIFY_VERSION;
Test::Person obj;
obj.set_name("gongluck");
obj.set_id(1);
*obj.mutable_email() = "https://github.com.cnpmjs.org/gongluck/CVIP";
len = obj.ByteSize();
cout << "len = " << len << endl;
obj.SerializeToArray(buf, len);
Test::Person obj2;
obj2.ParseFromArray(buf, len);
cout << "name = " << obj2.name() << endl;
cout << "id = " << obj2.id() << endl;
cout << "email = " << obj2.email() << endl;
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
4.消息队列
4.1 消息队列比较
4.2 zmq编译安装
#安装编译依赖
sudo apt-get install libtool pkg-config build-essential autoconf automake
#安装加密库
git clone git://github.com/jedisct1/libsodium.git
cd libsodium
./autogen.sh -s
./configure && make -j 8 && make check
sudo make install
sudo ldconfig
cd ..
#下载
git clone https://github.com/zeromq/libzmq.git
cd libzmq
#查看tag
#git tag
#版本 获取指定的版本
git checkout v4.3.2
./autogen.sh
./configure && make -j 8 && make check
sudo make install
sudo ldconfig
cd ..
4.3 zmq例子
-
/* * @Author: gongluck * @Date: 2020-11-24 09:49:11 * @Last Modified by: gongluck * @Last Modified time: 2020-11-24 09:51:26 */ // gcc zmqserver.c -lzmq #include <zmq.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <assert.h> int main(void) { // Socket to talk to clients void *context = zmq_ctx_new(); // 与客户端通信的套接字 void *responder = zmq_socket(context, ZMQ_REP); int rc = zmq_bind(responder, "tcp://*:5555"); assert(rc == 0); while (1) { // 等待客户端请求 char buffer[10]; zmq_recv(responder, buffer, 10, 0); printf("收到 %.*s\n", 10, buffer); // 返回应答 zmq_send(responder, "RECVED", 6, 0); } return 0; }
-
/* * @Author: gongluck * @Date: 2020-11-24 09:52:58 * @Last Modified by: gongluck * @Last Modified time: 2020-11-24 09:56:14 */ //gcc zmqclient.c -lzmq #include <zmq.h> #include <string.h> #include <stdio.h> #include <unistd.h> int main(void) { printf("Connecting to zmq server...\n"); void *context = zmq_ctx_new(); // 连接⾄服务端的套接字 void *requester = zmq_socket(context, ZMQ_REQ); zmq_connect(requester, "tcp://localhost:5555"); char buffer[10]; for (int request_nbr = 0; request_nbr != 10; request_nbr++) { printf("正在发送 %d...\n", request_nbr); zmq_send(requester, "Hello", 5, 0); zmq_recv(requester, buffer, 10, 0); printf("接收到 %.*s\n", 10, buffer); } zmq_close(requester); zmq_ctx_destroy(context); return 0; }
5.OpenSSL
5.1 OpenSSL编译安装
wget https://www.openssl.org/source/old/1.1.0/openssl-1.1.0l.tar.gz
tar xzvf OpenSSL-1.1.0l.tar.gz
cd OpenSSL-1.1.0l
./config --prefix=/usr/local/OpenSSL
make -j 8
sudo make install
5.2 OpenSSL例子
/*
* @Author: gongluck
* @Date: 2020-11-24 18:44:16
* @Last Modified by: gongluck
* @Last Modified time: 2020-11-24 18:53:20
*/
// gcc test.c -lssl -lcrypto
#include <stdio.h>
#include <string.h>
#include <openssl/lhash.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/rsa.h>
#define NAME_LENGTH 32
typedef struct _Person
{
char name[NAME_LENGTH];
int high;
char otherInfo[NAME_LENGTH];
} Person;
// 自定义比较函数
static int person_cmp(const void *a, const void *b)
{
char *namea = ((Person *)a)->name;
char *nameb = ((Person *)b)->name;
return strcmp(namea, nameb);
}
void print_value(void *a)
{
Person *p = (Person *)a;
printf("name: %s\n", p->name);
printf("high: %d\n", p->high);
printf("other info : %s\n", p->otherInfo);
}
int main()
{
// hash
OPENSSL_LHASH *h = lh_new(NULL, person_cmp);
if (h == NULL)
{
printf("err.\n");
return -1;
}
Person p1 = {"gongluck", 170, "xxxx"};
Person p2 = {"ben", 175, "xxxx"};
Person p3 = {"ken", 170, "xxxx"};
Person p4 = {"dio", 170, "xxxx"};
lh_insert(h, &p1);
lh_insert(h, &p2);
lh_insert(h, &p3);
lh_insert(h, &p4);
lh_doall(h, print_value);
void *data = lh_retrieve(h, (const char *)"dio"); //person_cmp return 0
if (data == NULL)
{
return -1;
}
print_value(data);
lh_free(h);
printf("\n------------------------------\n");
//bio
BIO *b = BIO_new(BIO_s_mem());
int len = BIO_write(b, "OpenSSL", 4);
len = BIO_printf(b, "%s", "gongluck");
printf("len: %d\n", len);
char *out = OPENSSL_malloc(len);
len = BIO_read(b, out, len);
printf("%s, len: %d\n", out, len);
OPENSSL_free(out);
BIO_free(b);
printf("\nsocket bio\n");
int sock = BIO_get_accept_socket("8899", 0);
BIO *bsock = BIO_new_socket(sock, BIO_NOCLOSE);
char *addr = NULL;
int ret = BIO_accept(sock, &addr);
BIO_set_fd(bsock, ret, BIO_NOCLOSE);
while (1)
{
char out[128] = {0};
BIO_read(bsock, out, 128);
if (out[0] = 'q')
break;
printf("%s\n", out);
}
BIO_free(bsock);
printf("\n------------------------------\n");
//base64
unsigned char in[30], base64[40], decode[30];
EVP_ENCODE_CTX *ectx = EVP_ENCODE_CTX_new();
EVP_EncodeInit(ectx);
for (int i = 0; i < 30; i++)
{
in[i] = i;
}
int outl, inl = 30;
EVP_EncodeUpdate(ectx, base64, &outl, in, inl);
EVP_EncodeFinal(ectx, base64 + outl, &outl);
EVP_ENCODE_CTX_free(ectx);
printf("%40s\n", base64);
printf("\n------------------------------\n");
//rsa
unsigned char inbuf[] = "https://github.com/gongluck/CVIP";
unsigned char outbuf[128] = {0};
int n = strlen(in);
MD4(inbuf, n, outbuf);
printf("MD4 result: \n"); // 16 byte
for (int i = 0; i < 16; i++)
{
printf("%x", outbuf[i]);
}
printf("\n");
MD5(inbuf, n, outbuf);
printf("MD5 result: \n"); // 16 byte
for (int i = 0; i < 16; i++)
{
printf("%x", outbuf[i]);
}
printf("\n");
// SHA(inbuf, n, outbuf);
// printf("SHA result: \n"); // 20 byte
// for (int i = 0; i < 20; i++)
// {
// printf("%x", outbuf[i]);
// }
// printf("\n");
SHA1(inbuf, n, outbuf);
printf("SHA1 result: \n"); // 20 byte
for (int i = 0; i < 20; i++)
{
printf("%x", outbuf[i]);
}
printf("\n");
SHA256(inbuf, n, outbuf);
printf("SHA256 result: \n"); // 32 byte
for (int i = 0; i < 32; i++)
{
printf("%x", outbuf[i]);
}
printf("\n");
SHA512(inbuf, n, outbuf);
printf("SHA512 result: \n"); // 64 byte
for (int i = 0; i < 64; i++)
{
printf("%x", outbuf[i]);
}
printf("\n");
return 0;
}
6.网络IO管理
6.1 信号驱动IO
/*
* @Author: gongluck
* @Date: 2020-11-26 08:05:17
* @Last Modified by: gongluck
* @Last Modified time: 2020-11-26 08:19:07
*/
// test : nc -uv 127.0.0.1 9096
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
int sockfd = 0;
void do_sigio(int sig)
{
struct sockaddr_in cli_addr;
int clilen = sizeof(struct sockaddr_in);
char buffer[256] = {0};
int len = recvfrom(sockfd, buffer, 256, 0, (struct sockaddr *)&cli_addr, (socklen_t *)&clilen);
printf("Message : %s\r\n", buffer);
int slen = sendto(sockfd, buffer, len, 0, (struct sockaddr *)&cli_addr, clilen);
}
int main(int argc, char *argv[])
{
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sigaction sigio_action;
sigio_action.sa_flags = 0;
sigio_action.sa_handler = do_sigio;
sigaction(SIGIO, &sigio_action, NULL);//SIGIO call do_sigio
struct sockaddr_in serv_addr = {0};
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9096);
serv_addr.sin_addr.s_addr = INADDR_ANY;
fcntl(sockfd, F_SETOWN, getpid());
int flags = fcntl(sockfd, F_GETFL, 0);
flags |= O_ASYNC | O_NONBLOCK;//异步非阻塞
fcntl(sockfd, F_SETFL, flags);
bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
while (1)
sleep(1);
close(sockfd);
return 0;
}
6.2 select IO
/*
* @Author: gongluck
* @Date: 2020-11-26 08:36:17
* @Last Modified by: gongluck
* @Last Modified time: 2020-11-26 08:39:02
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#define BUFFER_LENGTH 1024
int main(int argc, char *argv[])
{
int port = 9096;
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
perror("socket");
return -1;
}
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listenfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
{
perror("bind");
return -2;
}
if (listen(listenfd, 5) < 0)
{
perror("listen");
return -3;
}
fd_set rfds, rset;
FD_ZERO(&rfds);
FD_SET(listenfd, &rfds);
int max_fd = listenfd;
int i = 0;
while (1)
{
rset = rfds;
int nready = select(max_fd + 1, &rset, NULL, NULL, NULL);
if (nready < 0)
{
printf("select error : %d\n", errno);
continue;
}
if (FD_ISSET(listenfd, &rset)) // listen的fd可读代表有连接到达
{
struct sockaddr_in client_addr = {0};
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
if (clientfd <= 0)
continue;
char str[INET_ADDRSTRLEN] = {0};
printf("recvived from %s at port %d, listenfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
ntohs(client_addr.sin_port), listenfd, clientfd);
if (max_fd == FD_SETSIZE) // select的fd上限一般是1024
{
printf("clientfd --> out range\n");
break;
}
FD_SET(clientfd, &rfds); // 将新连接fd加入到读取队列中
if (clientfd > max_fd)
max_fd = clientfd;
printf("listenfd:%d, max_fd:%d, clientfd:%d\n", listenfd, max_fd, clientfd);
if (--nready == 0)
continue;
}
for (i = listenfd + 1; i <= max_fd; i++)
{
if (FD_ISSET(i, &rset))
{
char buffer[BUFFER_LENGTH] = {0};
int ret = recv(i, buffer, BUFFER_LENGTH, 0);
if (ret < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK) // 实际可能出现被其他线程读取掉数据的情况
{
printf("read all data");
}
FD_CLR(i, &rfds);
close(i);
}
else if (ret == 0)
{
printf("disconnect %d\n", i);
FD_CLR(i, &rfds);
close(i);
break;
}
else
{
printf("Recv: %s, %d Bytes\n", buffer, ret);
}
if (--nready == 0)
break;
}
}
}
return 0;
}
6.3 poll IO
/*
* @Author: gongluck
* @Date: 2020-11-26 08:36:17
* @Last Modified by: gongluck
* @Last Modified time: 2020-11-26 08:44:12
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/poll.h>
#include <errno.h>
#include <fcntl.h>
#define BUFFER_LENGTH 1024
#define POLL_SIZE 1024
int main(int argc, char *argv[])
{
int port = 9096;
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
perror("socket");
return -1;
}
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listenfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
{
perror("bind");
return -2;
}
if (listen(listenfd, 5) < 0)
{
perror("listen");
return -3;
}
struct pollfd fds[POLL_SIZE] = {0};
fds[0].fd = listenfd;
fds[0].events = POLLIN;
int max_fd = 0, i = 0;
for (i = 1; i < POLL_SIZE; i++)
{
fds[i].fd = -1;
}
while (1)
{
int nready = poll(fds, max_fd + 1, 5);
if (nready <= 0)
continue;
if ((fds[0].revents & POLLIN) == POLLIN) // 判断listenfd是否有数据可读(新连接)
{
struct sockaddr_in client_addr = {0};
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
if (clientfd <= 0)
continue;
char str[INET_ADDRSTRLEN] = {0};
printf("recvived from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
ntohs(client_addr.sin_port), listenfd, clientfd);
fds[clientfd].fd = clientfd;
fds[clientfd].events = POLLIN;
if (clientfd > max_fd)
max_fd = clientfd;
if (--nready == 0)
continue;
}
for (i = listenfd + 1; i <= max_fd; i++)
{
if (fds[i].revents & (POLLIN | POLLERR))
{
char buffer[BUFFER_LENGTH] = {0};
int ret = recv(i, buffer, BUFFER_LENGTH, 0);
if (ret < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
printf("read all data");
}
close(i);
fds[i].fd = -1;
}
else if (ret == 0)
{
printf(" disconnect %d\n", i);
close(i);
fds[i].fd = -1;
break;
}
else
{
printf("Recv: %s, %d Bytes\n", buffer, ret);
}
if (--nready == 0)
break;
}
}
}
return 0;
}
6.4 epoll IO
/*
* @Author: gongluck
* @Date: 2020-11-26 08:36:17
* @Last Modified by: gongluck
* @Last Modified time: 2020-11-26 08:52:01
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <fcntl.h>
#define BUFFER_LENGTH 1024
#define EPOLL_SIZE 1024
int main(int argc, char *argv[])
{
int port = 9096;
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0)
{
perror("socket");
return -1;
}
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listenfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0)
{
perror("bind");
return -2;
}
if (listen(listenfd, 5) < 0)
{
perror("listen");
return -3;
}
int epoll_fd = epoll_create(EPOLL_SIZE);
struct epoll_event ev, events[EPOLL_SIZE] = {0};
ev.events = EPOLLIN;
ev.data.fd = listenfd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listenfd, &ev);
while (1)
{
int nready = epoll_wait(epoll_fd, events, EPOLL_SIZE, -1);
if (nready == -1)
{
printf("epoll_wait\n");
break;
}
for (int i = 0; i < nready; i++)
{
if (events[i].data.fd == listenfd)
{
struct sockaddr_in client_addr = {0};
socklen_t client_len = sizeof(client_addr);
int clientfd = accept(listenfd, (struct sockaddr *)&client_addr, &client_len);
if (clientfd <= 0)
continue;
char str[INET_ADDRSTRLEN] = {0};
printf("recvived from %s at port %d, sockfd:%d, clientfd:%d\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)),
ntohs(client_addr.sin_port), listenfd, clientfd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = clientfd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, clientfd, &ev);
}
else
{
int clientfd = events[i].data.fd;
char buffer[BUFFER_LENGTH] = {0};
int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
if (ret < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
printf("read all data");
}
close(clientfd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = clientfd;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, &ev);
}
else if (ret == 0)
{
printf(" disconnect %d\n", clientfd);
close(clientfd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = clientfd;
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, clientfd, &ev);
break;
}
else
{
printf("Recv: %s, %d Bytes\n", buffer, ret);
}
}
}
}
return 0;
}
Last modified on 2020-11-30