C/C++常用方法

目录

    • C/C++开发常用github开源项目
    • C++表示二进制,八进制,十进制和十六进制
    • 两个结构体一起new
    • 函数传指针和指针引用
    • 1字节对齐
    • 四字节转int
    • C++十六进制字符串转换为十进制
    • 模拟main函数传参
    • 模拟main函数传参2
    • va_list可变参数列表
    • fork()多进程
    • 指针类型占多少字节
    • 指针一维数组和二维数组的分配与释放
    • 负数的二进制表示法
    • C++位运算
    • 打印当前系统时间
    • 睡眠sleep
    • C++生成随机数
    • C++取模均匀分配下标
    • fprintf()中的stdout、stderr
    • 打印进程ID和线程ID
    • char*与string相互转换
    • 字符串和数字相互转换
    • 获取可执行文件绝对路径
    • int、short、long、long long、 unsigned int、unsigned short、unsigned long、unsigned long long字节数,取值范围,打印方式
    • 纯C语言tcp客户端
    • 纯C语言tcp服务端

C/C++开发常用github开源项目

C++流媒体服务器SRS
https://github.com/ossrs

C++流媒体服务器ZLM
https://github.com/ZLMediaKit/ZLMediaKit

C/C++/QT音视频客户端
https://github.com/metartc/metaRTC

SocketIO
https://github.com/socketio/socket.io-client-cpp

C++操作JSON库:jsoncpp和picojson
https://github.com/open-source-parsers/jsoncpp/tree/master/src/jsontestrunner
https://github.com/kazuho/picojson/tree/master

云风写的C语言协程库
https://github.com/cloudwu/coroutine

京东青龙
https://github.com/6dylan6/jdpro

C++连接redis数据库
https://github.com/redis/hiredis
https://github.com/sewenew/redis-plus-plus

C语言实现traceroute功能
https://github.com/bgylde/traceroute.git

C语言实现ping
https://github.com/coding-fans/linux-network-programming/blob/master/src/c/icmp/ping/ping.c

C语言实现netstat命令功能
https://github.com/LipiLee/netstat/blob/master/netstat.c

C++表示二进制,八进制,十进制和十六进制

十进制,必须以非零数字开头。
八进制,任何以 0 开头的数字(包括普通 0)。
二进制,需要前缀 0b 或 0B。
十六进制,需要前缀 0x 或 0X。
demo:

int aa = 1001;//十进制
printf("aa=%d\n",aa);
    
int bb = 0b1001;//二进制
printf("bb=%d\n",bb);

int cc = 040;//八进制
printf("cc=%d\n",cc);

int dd = 0x20;//十六进制
printf("dd=%d\n",dd);

打印

aa=1001
bb=9
cc=32
dd=32

两个结构体一起new

两个结构体分配的空间是连续的,可以共用一个起始地址指针pBuff 。

struct HeadDef{
    int      MsgType;
    int      TotalLen;
    uint8_t     pData[0];	//地址指针标识,不占用空间
};

struct MsgDef{
    char        name[64];
    int         age;
};

    void *pBuff = new char(sizeof(HeadDef) + sizeof(MsgDef));
    HeadDef *pMsg = (HeadDef*)pBuff;
    pMsg->MsgType = 102;
    pMsg->TotalLen = sizeof(HeadDef) + sizeof(MsgDef);

    MsgDef *pMsg_body = (MsgDef*)pMsg->pData;
    pMsg_body->age = 20;
    std::string strname = "chw";
    memcpy(pMsg_body->name,strname.c_str(),strname.size());

    printf("pMsg->MsgType=%d\n",pMsg->MsgType);
    printf("pMsg->TotalLen=%d\n",pMsg->TotalLen);
    printf("pMsg_body->age=%d\n",pMsg_body->age);
    printf("pMsg_body->name=%s\n",pMsg_body->name);

打印:

pMsg->MsgType=102
pMsg->TotalLen=76
pMsg_body->age=20
pMsg_body->name=chw

函数传指针和指针引用

//传指针
//传的指针是行参,pNum指针自己的地址和实参指针地址不同,但指针指向的地址相同,因此可以使用行参改变指向地址的内容
//在这里对行参pNum做赋值操作是不会改变实参的
void testformal(int *pNum)
{
    printf("行参,指针自己的地址:%p\n",&pNum);
    printf("行参,指针指向的地址:%p\n",pNum);
    pNum = nullptr;//不会改变实参
}

//传指针引用
//传的是指针的引用,pNum指针地址、指针指向的地址均和实参一致
//在这里,不但可以改变实参指向地址的值,也可以改变实参本身
void testquote(int *&pNum)
{
    printf("引用,指针自己的地址:%p\n",&pNum);
    printf("引用,指针指向的地址:%p\n",pNum);
    pNum = nullptr;//会改变实参
}

    int *p = new int;
    printf("实参:指针自己的地址:%p\n",&p);
    printf("实参:指针指向的地址:%p\n",p);
    testformal(p);
    printf("%s\n",p == nullptr ? "p == nullptr" : "p != nullptr");
    testquote(p);
    printf("%s\n",p == nullptr ? "p == nullptr" : "p != nullptr");

打印

实参:指针自己的地址:0x7ffd0fe22f30
实参:指针指向的地址:0x562c56ea1e00
行参,指针自己的地址:0x7ffd0fe22f08
行参,指针指向的地址:0x562c56ea1e00
p != nullptr
引用,指针自己的地址:0x7ffd0fe22f30
引用,指针指向的地址:0x562c56ea1e00
p == nullptr

1字节对齐

当数据用于网络发送时,需要进行字节对齐

#pragma pack(push, 1)
//struct{...}
#pragma pack(pop)

四字节转int

int byte2int(uint8_t *data)
{
    int value = (int)((data[3] & 0xFF)
        | ((data[2] & 0xFF) << 8)
        | ((data[1] & 0xFF) << 16)
        | ((data[0] & 0xFF) << 24));
    return value;
}

调用

    char arr[4];
    arr[0] = 1;
    arr[1] = 1;
    arr[2] = 1;
    arr[3] = 1;

    int num = byte2int((uint8_t *)arr);
    printf("num=%d\n",num);

打印

num=16843009

C++十六进制字符串转换为十进制

    char *str = (char*)"0xff";//不区分大小写,0x可以不写
    int n = strtol(str, NULL, 16);
    printf("%d\n", n);

打印输出:255

模拟main函数传参

void parse_param(int argc, char **argv)
{
    for(int index=0;index<argc;index++)
    {
        printf("the %d param is [%s]\n",index,argv[index]);
    }
}

int argc = 2;
char *argv[2];
argv[0] = (char*)"chw";
argv[1] = (char*)"192.168.1.108";

parse_param(argc, argv);

打印:

the 0 param is [chw]
the 1 param is [192.168.1.108]

模拟main函数传参2

#include 
    int argc = 3;
    char *argv[3];
    argv[0] = (char*)"testPro";
    argv[1] = (char*)"-b";
    argv[2] = (char*)"chw";

    int opt;
    char *c;
    while ((opt = getopt(argc, argv, "b:p")) != EOF)
    {
        switch (opt)
        {
        case 'b':
            c = strdup(optarg);
            printf("b=%s\n",c);
            free(c);
            break;
        }
    }

打印

b=chw

va_list可变参数列表

va_list:一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
va_start:对ap进行初始化,让ap指向可变参数表里面的第一个参数。第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“…”之前的那个参数;
va_arg: 获取参数。它的第一个参数是ap,第二个参数是要获取的参数的指定类型。按照指定类型获取当前参数,返回这个指定类型的值,然后把 ap 的位置指向变参表中下一个变量的位置;
va_end:释放指针,将输入的参数 ap 置为 NULL。通常va_start和va_end是成对出现。

void test_printf1(char *headData, char *format,...)
{
    char buff[4096];
    va_list args;

    memcpy(buff, headData, strlen(headData));
    va_start(args, format);
    //将格式化数据从可变参数列表写入缓冲区
    vsnprintf(buff+strlen(headData), 1024, format, args);
    va_end(args);

    printf("%s\n", buff);
}

void test_printf2(int num,...)
{
    va_list args;
    va_start(args,num);
    for(int i = 0; i < num; i++)
        printf("%d\n", va_arg(args, int));
    va_end(args);
}

    test_printf1((char *)"data", (char *)"len is %d, data is %s", 10, "helloworld");
    test_printf2(3,100,200,300);

打印

datalen is 10, data is helloworld
100
200
300

fork()多进程

fork是复制进程的函数,通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,也可以根据传入参数的不同,做不同的事。

pid_t  fork(void);

函数返回值pid_t, 就是int型。

在父进程中,返回新建子进程的进程ID;
在子进程中,返回0;
创建失败,返回负值。

多进程特点
1、fork 子进程后,一般情况是子进程先调度。
2、子进程和父进程共享代码段,堆栈数据和全局数据是独立的,进程空间的各段采用了写时复制技术。
3、父子进程之间可通过信号通信,比如退出时会发信号。

测试代码,循环创建10个子进程。

#include 
#include 
int main()
{
    int pid = 0;
    int cnt = 10;
    do
    {
        pid = fork();
        if(pid < 0)
        {
            continue;
        }

        if(pid == 0)
        {
            break;
        }

        if(pid > 0)
        {
            cnt --;
        }
    }
    while(cnt > 0);
    printf("pid=%d,rpid=%d\n",pid,getpid());

    while(1){}
}

打印

pid=0,rpid=3622
pid=3628,rpid=3618
pid=0,rpid=3628
pid=0,rpid=3623
pid=0,rpid=3619
pid=0,rpid=3627
pid=0,rpid=3624
pid=0,rpid=3625
pid=0,rpid=3621
pid=0,rpid=3626
pid=0,rpid=3620

指针类型占多少字节

内存是由字节组成的,每个字节都有一个地址编号。
指针变量主要是存放相同数据类型的变量的首地址,这里的地址就是指内存中某个字节的编号,而这个编号的确定是由地址总线决定的,操作系统的位数决定了指针变量所占的字节数。
如果是32位操作系统,也就是地址总线是32位,则它的寻址范围就是02^32(4GB,所以32位系统最多配4G内存,多了也没有用),每一个字节的编址就会由32个0或者1组成。
例:第1个字节的编址是32个0,最后1个的编址是32个1。一个字节有8位,32位则需要4个字节。
64 位 CPU 寻址范围则很大,理论最大的寻址空间为 2^64。

不管是什么数据类型:
1、在32位操作系统下,指针是占4个字节空间大小。
2、在64位操作系统下,指针是占8个字节空间大小。

指针一维数组和二维数组的分配与释放

以int型指针为例。

    //1.int型指针
    int *p=new int;
    *p = 12;
    printf("*p=%d\n",*p);
    //释放
    delete p;
    p = nullptr;

    //2.int型一维数组
    int iArrNum = 5;
    int *pArr=new int [iArrNum];//只动态分配空间,未初始化
    memset(pArr,0,iArrNum*sizeof(int));
    for(int index=0;index<iArrNum;index++)
        printf("pArr[%d]=%d\n",index,pArr[index]);
    //释放
    delete []pArr;
    pArr = nullptr;

    //3.int型二维数组
    int row = 3;//行
    int col = 4;//列
    //分配一个指针数组,首地址保存在pInt
    int  **pInt=new int* [row];
    //为指针数组的每个元素分配一个数组
    for(int i=0;i<row;i++)
    {
        pInt[i]=new int [col];
    }
    //遍历
    int n = 0;
    for(int i=0;i<row;i++)
        for(int j=0;j<col;j++)
            pInt[i][j] = n++;
    for(int i=0;i<row;i++)
        for(int j=0;j<col;j++)
             printf("pInt[%d][%d]=%d\n",i,j,pInt[i][j]);
    //释放
    for(int i=0;i<row;i++)
    {
        delete []pInt[i];
    }
    delete []pInt;

打印

*p=12
pArr[0]=0
pArr[1]=0
pArr[2]=0
pArr[3]=0
pArr[4]=0
pInt[0][0]=0
pInt[0][1]=1
pInt[0][2]=2
pInt[0][3]=3
pInt[1][0]=4
pInt[1][1]=5
pInt[1][2]=6
pInt[1][3]=7
pInt[2][0]=8
pInt[2][1]=9
pInt[2][2]=10
pInt[2][3]=11

负数的二进制表示法

正数的表示法,先转换成二进制,在前面补0。
例如5的表示法:
00000000 00000000 00000000 00000101

在计算机中,负数以原码的补码形式表达
1、原码
用符号位和数值表示带符号数,正数的符号位用“0”表示,负数的符号位用“1”表示,数值部分用二进制形式表示。
例如

00000000 00000000 00000000 00000101 是 5的原码。
10000000 00000000 00000000 00000101 是-5的原码。

2、反码
正数的反码与原码相同,负数的反码为对该数的原码除符号位外各位取反。
例如

正数00000000 00000000 00000000 00000101 的反码还是 00000000 00000000 00000000 00000101
负数10000000 00000000 00000000 00000101 的反码则是 11111111 11111111 11111111 11111010。

3、补码
正数的补码与原码相同,负数的补码为对该数的原码除符号位外各位取反,然后在最后一位加1。
例如

10000000 00000000 00000000 00000101 的反码是:11111111 11111111 11111111 11111010
补码为:
11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011
-5 在计算机中表达为:11111111 11111111 11111111 11111011。转换为十六进制:0xFFFFFFFB。

举例,int类型-1在计算机中表示法:

-1的原码:10000000 00000000 00000000 00000001
得反码: 11111111 11111111 11111111 11111110(除符号位按位取反)
得补码: 11111111 11111111 11111111 11111111

C++位运算

位是数据存储的最小单位,简记为b,也称为比特,每个0或1就是一个位(bit)。
位运算操作符:& (按位与)、| (按位或)、^ (按位异或)、~ (按位取反)、>> (按位右移)、<< (按位左移)。

&(按位与)
如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。

| (按位或)
如果两个相应的二进制位只要有一个是1,结果就是1;否则为0。

~(按位取反)
每一位进行取反运算,1变成0,0变成1。

^(按位异或)
两个相同的数会变成0,反之是1

>>(按位右移)
把二进制位整体向右移动,右移等于除以2的N次方,N为右移的位数。

<<(按位左移)
把二进制位整体向左移动,左移等于乘以2的N次方,N为左移的位数

demo

    int aa = 0b1001;//9
    int bb = 0b1110;//14

    int yu    = aa & bb;//1000,8
    int huo   = aa | bb;//1111,15
    int fei   = ~aa;    //-10
    int yihuo = aa ^ bb;//111,7
    int youyi = aa >> 1;//100,4
    int zuoyi = aa << 1;//10010,18

    printf("aa=%d\n",aa);
    printf("bb=%d\n",bb);
    printf("yu=%d\n",yu);
    printf("huo=%d\n",huo);
    printf("fei=%d\n",fei);
    printf("yihuo=%d\n",yihuo);
    printf("youyi=%d\n",youyi);
    printf("zuoyi=%d\n",zuoyi);

打印

aa=9
bb=14
yu=8
huo=15
fei=-10
yihuo=7
youyi=4
zuoyi=18

打印当前系统时间

linux系统

#include 
/* 获取时间,1970年1月1日到现在的时间 */
struct timeval time;
gettimeofday(&time, NULL);
printf("s: %ld, ms: %ld\n", time.tv_sec, (time.tv_sec*1000 + time.tv_usec/1000));

windows系统

#include 
SYSTEMTIME timenow;
GetLocalTime(&timenow);   printf("hour=%u,minute=%u,second=%u,Milliseconds=%d\n",timenow.wHour,timenow.wMinute,timenow.wSecond,timenow.wMilliseconds);

睡眠sleep

linux

#include 
#include 
#include 
int sleep_time = 5;
cout << "sleep before time: " << time(NULL) << endl;
sleep(sleep_time);//睡眠单位,秒
cout << "sleep after time:  " << time(NULL) << endl;

sleep_time = 10*1000;
cout << "======================" << endl;
struct timeval tv;
gettimeofday(&tv,NULL);
cout << "usleep before time: " << tv.tv_sec<<"s,"<<tv.tv_usec<<" 微秒"<<endl;
usleep(sleep_time);//睡眠单位,微秒
gettimeofday(&tv,NULL);
cout << "usleep after time:  " << tv.tv_sec<<"s,"<<tv.tv_usec<<" 微秒"<<endl;

打印

sleep before time: 1675737566
sleep after time:  1675737571
======================
usleep before time: 1675737571s,340562 微秒
usleep after time:  1675737571s,351354 微秒

windows

#include 
#include 
SYSTEMTIME timenow;
GetLocalTime(&timenow);
printf("hour=%u,minute=%u,second=%u,Milliseconds=%d\n",timenow.wHour,timenow.wMinute,timenow.wSecond,timenow.wMilliseconds);
Sleep(10);//单位毫秒
GetLocalTime(&timenow);
printf("hour=%u,minute=%u,second=%u,Milliseconds=%d\n",timenow.wHour,timenow.wMinute,timenow.wSecond,timenow.wMilliseconds);

打印

hour=10,minute=32,second=23,Milliseconds=515
hour=10,minute=32,second=23,Milliseconds=526

C++生成随机数

方法1:rand()
使用srand()设置种子,rand()生成随机数,如果种子一样则会生成相同的随机数;如果不设置种子则默认初始种子为1,生成的随机数做为新种子用于下次生成随机数。rand()生成的是伪随机数。

struct timeval time;
gettimeofday(&time, NULL);
unsigned long ms = (time.tv_sec*1000 + time.tv_usec/1000);

srand(ms);//设置种子
int random = rand();
printf("random=%d\n",random);
usleep(100*10);

方法2:random_device
random_device 是标准库提供到一个非确定性随机数生成器,使用硬件作为随机数来源,故其调用代价较高,一般用来产生随机数种子。
random_device提供()操作符,用来返回一个min()到max()之间的一个高质量随机数,可以理解为真随机数。

std::random_device rd;//做为随机数种子

//下面几种是标准库提供的伪随机数生成器(种子相同则随机数相同),使用random_device生成的真随机数做为种子是常用方法。
///1.mt19937 是标准库提供的采用梅森旋转算法的伪随机数生成器,可以快速产生高质量到随机数。
std::mt19937 mt(rd());
for(int n = 0; n < 10; n++)
    std::cout << mt() << std::endl;

///2.default_random_engine 是标准库提供的默认随机数生成器,其实现和编译器有关。
std::default_random_engine r_eng(rd());
for (int i = 0; i < 10; ++i)
  std::cout << r_eng() << std::endl;

///3.minstd_rand 是标准库提供的采用线性同余算法的伪随机数生成器。
std::minstd_rand   r_minstd_rand(rd());
for (int i = 0; i < 10; ++i)
  std::cout << r_minstd_rand() << std::endl;

///4.ranlux24_base 是标准库提供的采用带进位减法的伪随机数生成器。
std::ranlux24_base  r_ranlux24_base(rd());
for (int i = 0; i < 10; ++i)
  std::cout << r_ranlux24_base() << std::endl;

C++取模均匀分配下标

    int _Index = -1;
    int _taskpoll = 6;
    for(int index=0;index<10;index++)
    {
        _Index++;
        _Index %= _taskpoll;
        printf("_Index=%d\n",_Index);
    }

打印

_Index=0
_Index=1
_Index=2
_Index=3
_Index=4
_Index=5
_Index=0
_Index=1
_Index=2
_Index=3

fprintf()中的stdout、stderr

#include
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);

printf将格式化写到标准输出,fprintf写至指定的流,默认输出到屏幕。

#include 
setbuf(stdout,nullptr);//设置无缓存区,直接打印
setbuf(stderr,nullptr);
int day = 27;
fprintf(stdout, "Hello:%d \n",day);
const char *msg = "error hapd";
fprintf(stderr, "World!:%s\n",msg);

打印

Hello:27 
World!:error hapd

可以把流重定向到指定文件,下面运行可执行文件,输出流重定向到tmp.txt文件。

./fpint > tmp.txt

打印进程ID和线程ID

linux系统

printf("mainwindow thread tid = %lu\n", pthread_self());
#include
printf("mainwindow,pid:%u, tid:%u\n",(unsigned int)getpid(), (unsigned int)gettid());
#include 
printf("mainwindow,ThreadID: %lu\n", std::this_thread::get_id());

windows系统

#include 
printf("mainwindow,ThreadID: %lu\n", std::this_thread::get_id());

char*与string相互转换

#include//cout需要的头文件
using namespace std;
//char*转string
char ary[1024]="abcdefg";
string str=ary;
cout<<"str="<<str<<endl;//str=abcdefg

//string转char*
char ary2[1024];
strcpy(ary2,str.c_str());//建议用strcpy_s
printf("ary2=%s\n",ary2);//ary2=abcdefg

字符串和数字相互转换

//int转string,其他类型的整型和浮点型均可使用
int i = 42;
double d = 42.7;
string str = to_string(i);
string str2 = to_string(d);
printf("str=%s,str2=%s\n",str.c_str(),str2.c_str());//str=42,str2=42.700000

//string转int,也可转为其他类型的整型和浮点型
int ii = stoi(str);                     //int
long ll = stol(str);                    //long
unsigned long ull = stoul(str);         //unsigned long
long long lll = stoll(str);             //long long
unsigned long long ulll = stoull(str);  //unsigned long long
float ff = stof(str);                   //float
double dd = stod(str);                  //double
long double ldd = stold(str);           //long double

获取可执行文件绝对路径

#include //linux(全路径,不包含可执行文件名)
#include //win(包含可执行文件名的全路径)
#define MAXPATH 1000
char buffer[MAXPATH];
getcwd(buffer,MAXPATH);
printf("The current directoryis:%s\n",buffer);

int、short、long、long long、 unsigned int、unsigned short、unsigned long、unsigned long long字节数,取值范围,打印方式

//int型大小4字节,数值范围:-2^(32-1) – 2^(32-1)-1(即 -2147483648 ~ 2147483647)
int _int_max = 2147483647;
int _int_min = -2147483648 ;
printf("_int_max=%d,_int_max+1=%d\n",_int_max,_int_max+1);//_int_max=2147483647,_int_max+1=-2147483648
printf("_int_min=%d,_int_min-1=%d\n",_int_min,_int_min-1);//_int_min=-2147483648,_int_min-1=2147483647

//short型大小2字节,数值范围:-2^(16-1) – 2^(16-1) -1 (即 -32768 ~ 32767)
short _short_max = 32767;
short _short_min = -32768;
printf("_short_max=%hd,_short_max+1=%hd\n",_short_max,_short_max+1);//_short_max=32767,_short_max+1=-32768
 printf("_short_min=%hd,_short_min-1=%hd\n",_short_min,_short_min-1);//_short_min=-32768,_short_min-1=32767

//long型大小4字节,数值范围:-2^(32-1) – 2^(32-1)-1(即 -2147483648 ~ 2147483647)
long _long_max = 2147483647;
long _long_min = -2147483648 ;
printf("_long_max=%ld,_long_max+1=%ld\n",_long_max,_long_max+1);//_long_max=2147483647,_long_max+1=-2147483648
printf("_long_min=%ld,_long_min-1=%ld\n",_long_min,_long_min-1);//_long_min=-2147483648,_long_min-1=2147483647

//long long型大小8字节,数值范围:-2^(64-1) ~ 2^(64-1)-1(即 -9223372036854775808 ~ 9223372036854775807
long long _long_long_max = 9223372036854775807;
long long _long_long_min = -9223372036854775808;
//_long_long_max=9223372036854775807,_long_long_max+1=-9223372036854775808
//_long_long_min=-9223372036854775808,_long_long_min-1=9223372036854775807
printf("_long_long_max=%lld,_long_long_max+1=%lld\n",_long_long_max,_long_long_max+1);
printf("_long_long_min=%lld,_long_long_min-1=%lld\n",_long_long_min,_long_long_min-1);


//unsigned int型大小4字节,数值范围:0 – 2^(32)-1 (即 0~4294967295)
unsigned int _unsigned_int_max = 4294967295;
unsigned int _unsigned_int_min = 0 ;
printf("_unsigned int_max=%u,_unsigned int_max+1=%u\n",_unsigned_int_max,_unsigned_int_max+1);//_unsigned int_max=4294967295,_unsigned int_max+1=0
printf("_unsigned int_min=%u,_unsigned int_min-1=%u\n",_unsigned_int_min,_unsigned_int_min-1);//_unsigned int_min=0,_unsigned int_min-1=4294967295

//unsigned short型大小2字节,数值范围:0 ~ 2^16 -1 (即 0~65535)
unsigned short _unsigned_short_max = 65535;
unsigned short _unsigned_short_min = 0;
printf("_unsigned_short_max=%hu,_unsigned short_max+1=%hu\n",_unsigned_short_max,_unsigned_short_max+1);//_unsigned_short_max=65535,_unsigned short_max+1=0
printf("_unsigned_short_min=%hu,_unsigned short_min-1=%hu\n",_unsigned_short_min,_unsigned_short_min-1);//_unsigned_short_min=0,_unsigned short_min-1=65535

//long型大小4字节,数值范围:0 – 2^(32)-1 (即 0~4294967295)
unsigned long _unsigned_long_max = 4294967295;
unsigned long _unsigned_long_min = 0;
printf("_unsigned_long_max=%lu,_unsigned_long_max+1=%lu\n",_unsigned_long_max,_unsigned_long_max+1);//_unsigned_long_max=4294967295,_unsigned_long_max+1=0
printf("_unsigned_long_min=%lu,_unsigned_long_min-1=%lu\n",_unsigned_long_min,_unsigned_long_min-1);//_unsigned_long_min=0,_unsigned_long_min-1=4294967295

//unsigned long long型大小8字节,数值范围:0~2^64-1(即 0 ~ 18446744073709551615)
unsigned long long _unsigned_long_long_max = 18446744073709551615;
unsigned long long _unsigned_long_long_min = 0;
//_unsigned_long_long_max=18446744073709551615,_unsigned_long_long_max+1=0
//_unsigned_long_long_min=0,_unsigned_long_long_min-1=18446744073709551615
printf("_unsigned_long_long_max=%llu,_unsigned_long_long_max+1=%llu\n",_unsigned_long_long_max,_unsigned_long_long_max+1);
printf("_unsigned_long_long_min=%llu,_unsigned_long_long_min-1=%llu\n",_unsigned_long_long_min,_unsigned_long_long_min-1);
///   常用整型typedef
//    typedef short               int16_t;
//    typedef unsigned short      uint16_t;
//    typedef int                 int32_t;
//    typedef unsigned            uint32_t;
//    typedef long long           int64_t;
//    typedef unsigned long long  uint64_t;

纯C语言tcp客户端

g++ tcp_client.c -lpthread -o cli
./cli

tcp_client.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef _WIN32
#include 
#include 
#define socklen_t int
#else
#include 
#include 
#include 
#include 
#include 
#endif

typedef struct {
    int32_t fd;
    int32_t localPort;
    int32_t remotePort;
    int32_t isStart;
    int32_t isLoop;
    pthread_t threadId;

    //char serverIp[30];
    void* user;
    struct sockaddr_in local_addr;
    struct sockaddr_in remote_addr;
    void (*receive)(char *data, int32_t nb_data,void* user);
}TcpHandle;

int32_t create_tcpClient(TcpHandle *tcpClient, char *serverIp, int32_t serverPort, int32_t plocalPort)
{
    if (tcpClient == NULL)		return -1;

    tcpClient->localPort = plocalPort;
    tcpClient->fd = -1;
    tcpClient->remotePort = serverPort;

#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    WSAStartup(wVersionRequested, &wsaData);//初始化socket库
#endif

    tcpClient->local_addr.sin_family = AF_INET;
    tcpClient->local_addr.sin_port = htons(tcpClient->localPort);
    // lcl_addr.sin_addr.s_addr=inet_addr(ip);
    tcpClient->local_addr.sin_addr.s_addr = INADDR_ANY;
    if((tcpClient->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return -2;
    }

    tcpClient->remote_addr.sin_family = AF_INET;
    tcpClient->remote_addr.sin_port = htons(tcpClient->remotePort);

#ifdef _WIN32
    tcpClient->remote_addr.sin_addr.S_un.S_addr=inet_addr(serverIp);
#else
    tcpClient->remote_addr.sin_addr.s_addr = inet_addr(serverIp);//将点分十进制IP转换成网络字节序IP
#endif

    if(connect(tcpClient->fd, (struct sockaddr *)&tcpClient->remote_addr, sizeof(tcpClient->remote_addr)) < 0)
    {
        perror("connect");
        return -3;
    }
    
    printf("connect success\n");

    return 0;
}

void* recv_thread_tcpClient(void *obj) {
#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    WSAStartup(wVersionRequested, &wsaData);
#endif
    TcpHandle *ptcpClient = (TcpHandle*) obj;
    ptcpClient->isStart = 1;
#ifdef _WIN32
    int32_t timeout=200;
    setsockopt(ptcpClient->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout,  sizeof(timeout));
#else
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 20000;  // 20 ms
    setsockopt(ptcpClient->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv,	sizeof(struct timeval));
#endif
    char buffer[2048] = { 0 };
    ptcpClient->isLoop = 1;

    int32_t len = 0;
    socklen_t src_len = sizeof(struct sockaddr_in);

    while (ptcpClient->isLoop)
    {
        struct sockaddr_in src;
        memset(&src, 0, src_len);
        memset(buffer, 0, 2048);
        if ((len = recvfrom(ptcpClient->fd, buffer, 2048, 0,	(struct sockaddr*) &src, &src_len)) > 0)
        {
			printf("buffer=%s\n",buffer);
        }
    }
    ptcpClient->isStart = 0;
    #ifdef _WIN32
    closesocket(ptcpClient->fd);
    #else
    close(ptcpClient->fd);
    #endif
    ptcpClient->fd = -1;

    return NULL;
}

TcpHandle *pTcpHandle;
int main()
{
	pTcpHandle = (TcpHandle *)malloc(sizeof(TcpHandle));
    	create_tcpClient(pTcpHandle,(char*)"127.0.0.1",9090,0);
	
	pthread_create(&pTcpHandle->threadId, 0, recv_thread_tcpClient, pTcpHandle);
	
	std::string msg = "hello";
	send(pTcpHandle->fd,msg.c_str(),msg.size(),0);
	
	while(1){}
}

纯C语言tcp服务端

g++ tcp_server.c -lpthread -o ser
./ser

tcp_server.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef _WIN32
#define _WIN32_WINNT 0x0600
#include "windows.h"
#include 
#include 

#define socklen_t int
#else
#include 
#include 
#include 
#include 
#include 
#include 
#endif

#ifndef _WIN32
#include 
#define GetSockError()	errno
#define SetSockError(e)	errno = e

#define closesocket(s)	close(s)
#else
#define GetSockError()	WSAGetLastError()
#define SetSockError(e)	WSASetLastError(e)
#define setsockopt(a,b,c,d,e)	(setsockopt)(a,b,c,(const char *)d,(int)e)

#endif

typedef struct {
    int32_t serverfd;
    int32_t connFd;
    int32_t isStart;
    int32_t isLoop;
    pthread_t threadId;
    int32_t serverPort;
    void* user;
    char remoteIp[32];

    struct sockaddr_in local_addr;
    struct sockaddr_in remote_addr;
    void (*receive)(char *data, int32_t nb_data,void* user,int32_t clientFd);
    void (*startStunTimer)(void* user);
}TcpServer;

int32_t create_tcpServer(TcpServer *tcpServer, int32_t listenPort)
{
    if (tcpServer == NULL)
        return 1;

    tcpServer->serverfd = -1;
    tcpServer->serverPort = listenPort;
    tcpServer->local_addr.sin_family = AF_INET;
    tcpServer->local_addr.sin_port = htons(listenPort);
#ifdef _WIN32
    tcpServer->local_addr.sin_addr.S_un.S_addr=INADDR_ANY;
#else
    tcpServer->local_addr.sin_addr.s_addr = INADDR_ANY;
#endif
    return 0;
}

void* run_tcpConnClient_thread(void *obj)
{
    TcpServer* tcpServer=(TcpServer*)obj;
    int connfd=tcpServer->connFd;
    char remoteIp[32]={0};
    strcpy(remoteIp,tcpServer->remoteIp);

    int32_t nBytes =0;
    char buffer[2048] = { 0 };
    while (tcpServer->isLoop)
    {
        memset(buffer, 0, 2048);
        nBytes = recv(connfd, buffer, 2048, 0);

        if (nBytes > 0)
        {
            printf("buffer=%s\n",buffer);
            
            std::string msg = "back";
            send(connfd,msg.c_str(),msg.size(),0);
        }
        else if (nBytes == -1)
        {
            int32_t sockerr = GetSockError();
            if (sockerr == EINTR)
                continue;
            if (sockerr == EWOULDBLOCK || sockerr == EAGAIN)
            {
                nBytes = 0;
                continue;
            }
#ifdef Q_OS_LINUX
            printf("%s, recv returned %d. GetSockError(): %d (%s)\n",  __FUNCTION__, nBytes, sockerr, strerror(sockerr));
#endif

            continue;
        }
    }
    closesocket(connfd);

    return NULL;
}

void* run_tcpServer_thread(void *obj) {
#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    WSAStartup(wVersionRequested, &wsaData);
#endif
    TcpServer *tcpServer = (TcpServer*) obj;
    tcpServer->isStart = 1;
    tcpServer->serverfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#ifdef _WIN32
    int32_t timeout=200;
    setsockopt(tcpServer->serverfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout,  sizeof(timeout));
#else
    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 200000;  // 200 ms
    setsockopt(tcpServer->serverfd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv,
            sizeof(struct timeval));
#endif
    printf("http tcp server is starting,listenPort==%d\n", tcpServer->serverPort);
    int bReuseaddr = 1;
    setsockopt( tcpServer->serverfd, SOL_SOCKET, SO_REUSEADDR, ( const char* )&bReuseaddr, sizeof( int ) );

    if (bind(tcpServer->serverfd, (struct sockaddr*) &tcpServer->local_addr,sizeof(struct sockaddr_in)) < 0) {
        printf("http server bind error(%d)",GetSockError());
        exit(1);
    }
    listen(tcpServer->serverfd, 5);

    tcpServer->isLoop = 1;
    socklen_t src_len = sizeof(struct sockaddr_in);
    while (tcpServer->isLoop)
    {
        struct sockaddr_in src;
        memset(&src, 0, src_len);
        int connfd = accept(tcpServer->serverfd, (struct sockaddr*) &src, &src_len);
        if(connfd>-1)
        {
            pthread_t th;
            tcpServer->connFd=connfd;
            memset(tcpServer->remoteIp,0,sizeof(tcpServer->remoteIp));
#ifdef _WIN32
            inet_ntop(AF_INET,&src.sin_addr.s_addr, tcpServer->remoteIp, sizeof(tcpServer->remoteIp));
#else
            inet_ntop(AF_INET,&src.sin_addr.s_addr, tcpServer->remoteIp, sizeof(tcpServer->remoteIp));
#endif

            pthread_create(&th, 0, run_tcpConnClient_thread, tcpServer);
        }
    }
    tcpServer->isStart = 0;
#ifdef _WIN32
        closesocket(tcpServer->serverfd);
#else
        close(tcpServer->serverfd);
#endif
    tcpServer->serverfd = -1;

    return NULL;
}

int main()
{
	TcpServer* pTcpServer = (TcpServer *)malloc(sizeof(TcpServer));
    	create_tcpServer(pTcpServer,9090);

	pthread_create(&pTcpServer->threadId, 0, run_tcpServer_thread, pTcpServer);
	
	while(1){}
}

你可能感兴趣的:(C/C++,c++,c语言,算法,linux,开发语言)