在实际开发中经常用到web框架,比如Servlet,SpringBoot等,这些开发框架提高了我们的开发效率,节省了开发时间。但是这会令我们技术人员处于浮云之上,看不到其本质。说实话,Java语言那么流行,其本质是运行在JRE虚拟机上的,而JRE是用C/C++语言开发的。与其说java跨平台,不如说是因为在不同平台上各自实现JRE,从而屏蔽了java语言直接与不同平台打交道。http协议广泛应用,也是基于TCP协议之上的封装。本节博主将带领大家用C语言在Linux环境下开发HTTP服务器,支持浏览器下载和浏览文件。另外还使用TCP协议开发了服务端和客户端来实现服务端监听客户端连接,然后向其发送一首唐诗。

目录

1. 关键函数说明

1.1 IP地址字节序转换

1.2 端口字节序转换

1.3 socket

1.4 bind

1.5 listen

1.6 accept

1.7 接收数据

1.8 发送数据

1.9 connect

2. HTTP服务器

2.1 源码

2.2 效果

3.TCP服务器和客户端

3.1 源码

3.2 效果


1. 关键函数说明

1.1 IP地址字节序转换

IP 地址本质是整数,但是为了方便,在使用的过程中都是用字符串来描述,超过两个字节的数据单元,在跨网络传输时候就需要考虑本地字节序和网络字节序的转换,Linux下主要使用api如下:

1)本地字节序转网络字节序
int inet_pton(int af, const char *src, void *dst); 
参数:
af: 地址族协议,IPV4或者IPV6
AF_INET: IPV4  地址
AF_INET6: IPV6地址
src: 点分十进制的 ip 地址,例如192.168.1.2
dst: 传出参数,存放大端整形IP地址
返回值:成功返回 1,失败返回 0 或者 - 1

2)网络字节序转本机字节序    
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

参数
af: 地址族协议,IPV4或者IPV6
AF_INET: IPV4  地址
AF_INET6: IPV6地址
src: 大端的整形 IP 地址
dst: 存储转换得到的小端的点分十进制的IP地址
size: dst内存中占用字节数
返回值:
成功:指针指向第三个参数对应的内存地址,通过返回值可以直接取出转换得到的IP字符串
失败: NULL

只能处理IPV4地址的api
(1)点分十进制IP转大端整形
in_addr_t inet_addr (const char *cp);

(2)大端整形转点分十进制IP
char* inet_ntoa(struct in_addr in);

1.2 端口字节序转换

1)主机字节序转网络字节序
uint16_t htons(uint16_t hostshort);    
uint32_t htonl(uint32_t hostlong);

2)网络字节序转主机字节序
uint16_t ntohs(uint16_t netshort)
uint32_t ntohl(uint32_t netlong);

1.3 socket

int socket(int domain, int type, int protocol);

作用:创建socket文件描述符,通过该文件描述符可以操作内核中的某一块内存,用来进行网络通信

参数:
domain: 使用的地址族协议
AF_INET:  IPv4 格式的 ip 地址
AF_INET6:  IPv6 格式的 ip 地址
type:
SOCK_STREAM: 流式传输协议
SOCK_DGRAM: 报式 (报文) 传输协议
protocol: 一般写 0 ,表示使用默认的协议
SOCK_STREAM: 流式传输默认是 tcp
SOCK_DGRAM: 报式传输默认是udp
返回值:
成功:可用于套接字通信的文件描述符
失败: -1

1.4 bind

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

作用:将文件描述符和本地的IP与端口进行绑定

参数:
sockfd: 监听的文件描述符,通过 socket () 调用得到
addr: 要绑定的 IP 和端口信息需要初始化到这个结构体中,IP和端口要转换为网络字节序
addrlen: 参数 addr 指向的内存大小
返回值:成功返回 0,失败返回 - 1

1.5 listen

作用:监听套接字
int listen(int sockfd, int backlog);
参数:
sockfd: 文件描述符,调用 socket () 得到
backlog: 同时能处理的最大连接,最大值为 128
返回值:成功返回 0,失败返回 -1

1.6 accept

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
作用:等待并接受客户端的连接请求, 建立新的连接, 得到一个通信的文件描述符,函数是一个阻塞函数,当没有新的客户端连接请求的时候,该函数阻塞。
当检测到有新的客户端连接时,解除阻塞,得到的描述符就可以和客户端通信。
参数:
sockfd: 监听的文件描述符
addr: 建立连接的客户端的地址信息
addrlen:  addr 指向的内存大小
返回值:函数调用成功,得到一个文件描述符,调用失败返回 -1

1.7 接收数据

ssize_t read(int sockfd, void *buf, size_t size);
ssize_t recv(int sockfd, void *buf, size_t size, int flags);

参数:
sockfd: accept 或者connect函数的返回值
buf: 接收数据内存
size: 参数 buf 指向的内存的容量
flags: 一般不使用,指定为 0
返回值:
大于 0:实际接收的字节数
等于 0:对方断开了连接
-1:接收数据失败了
如果连接没有断开,接收不到数据,会阻塞等待数据到达,数据到达后函数解除阻塞,开始接收数据,当发送端断开连接,接收端无法接收到任何数据,时候就不会阻塞了,直接返回0。

1.8 发送数据

ssize_t write(int fd, const void *buf, size_t len);
ssize_t send(int fd, const void *buf, size_t len, int flags);
参数:
fd: accept 或者connect函数的返回值
buf: 发送的数据
len: 要发送数据长度
flags: 一般不使用,指定为 0
返回值:
大于 0:实际发送的字节数,等于参数 len
-1:发送数据失败了

1.9 connect

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
作用:与服务器建立连接
参数:
sockfd: socket 返回值
addr: 要连接的服务器的 iP 和 端口,这个 IP 和端口都是大端的
addrlen: addr内存大小
返回值:连接成功返回 0,连接失败返回 - 1

2. HTTP服务器

http服务器实现建立在tcp协议之上,采用epoll和多线程的方式接收和处理客户端。解析从TCP传过来的数据,解析请求行,找到需要访问的资源。如果是目录则使用html的a标签href属性进行重定位,进入目录。如果是文件,txt和png文件浏览器在线预览,其他文件则让浏览器进行下载。

2.1 源码

头文件:

#pragma once
#include <pthread.h>//线程参数结构
struct ThreadParam {pthread_t tid;int fd;int epfd;
};int initListenFd(unsigned short port);int epoolRun(int lfd);void* acceptClient(void *);void* recvHttpRequest(void *);int parseRequestLine(int cfd, const char *line);int sendFile(int cfd, const char *fileName);int sendHeadMsg(int cfd, int status, const char *descr, const char *type, int length);const char *getFileType(const char *name);int sendDir(int cfd, const char*dirName);int hex2dec (char c);char dec2hex (short int c);void urlDecode(char* from, char *to);

源文件

#include "HttpServer.h"
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <sys/sendfile.h>
#include <dirent.h>
#include <ctype.h>int initListenFd(unsigned short port) {int lfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == lfd) {perror("scoket");return -1;}int opt = -1;int ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));if (-1 == ret) {perror("setsockopt");return -1;}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = INADDR_ANY;ret = bind(lfd, (const struct sockaddr *)&addr, sizeof(addr));if (-1 == ret) {perror("bind");return -1;}ret = listen(lfd, 128);if (-1 == ret) {perror("listen");return -1;}return lfd;
}int epoolRun(int lfd) {int epfd = epoll_create(1);if (-1 == epfd) {perror("epoll_create");return -1;}struct epoll_event ev;ev.data.fd = lfd;ev.events = EPOLLIN;int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);if (-1 == ret) {perror("epoll_ctl");return -1;}struct epoll_event evs[2048];int maxwait = sizeof(evs) / sizeof(struct epoll_event);while (1) {int num = epoll_wait(epfd, evs, maxwait, -1);for (int i = 0; i <= num; i++) {int fd = evs[i].data.fd;pthread_t tid;struct ThreadParam *param = (struct ThreadParam *)malloc(sizeof(struct ThreadParam));param->fd = fd;param->epfd = epfd;param->tid = tid;if (fd == lfd) {pthread_create(&tid, NULL, acceptClient, param);pthread_detach(tid);}else {pthread_create(&tid, NULL, recvHttpRequest, param);pthread_detach(tid);}}}return 0;
}void* acceptClient(void *arg) {struct ThreadParam *param = (struct ThreadParam *)arg;if (!param) {return NULL;}int lfd = param->fd;int epfd = param->epfd;int cfd = accept(lfd, NULL, NULL);if (-1 == cfd) {perror("accept");return NULL;}int flag = fcntl(cfd, F_GETFL);flag |= O_NONBLOCK;fcntl(cfd, F_SETFL, flag);struct epoll_event ev;ev.data.fd = cfd;ev.events = EPOLLIN | EPOLLET; //cfd边缘非阻塞模式,效率最高int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &ev);if (-1 == cfd) {perror("epoll_ctl");}free(param);param = NULL;printf("accept thread %ld\n", pthread_self());return NULL;
}void* recvHttpRequest(void *arg) {struct ThreadParam *param = (struct ThreadParam *)arg;char buf[8192] = {0};int len = 0;int total = 0;char tmpBuf[1024] = {0};if (!param) {return NULL;}int cfd = param->fd;int epfd = param->epfd;while ((len = recv(cfd, tmpBuf, sizeof(tmpBuf), 0)) > 0) {if (total + len < sizeof(buf)) {memcpy(buf + total, tmpBuf, len);}total += len;}if (-1 == len && errno == EAGAIN && total > 0) { //接收数据完毕//解析http协议char *pt = strstr(buf, "\r\n");int reqLen = pt - buf;buf[reqLen] = '\0';parseRequestLine(cfd, buf);}else if (0 == len) { //客户端断开了连接epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL);close(cfd);}else {perror("recv");}free(param);param = NULL;printf("client thread %ld\n", pthread_self());return NULL;
}int parseRequestLine(int cfd, const char *line) {char method[32] = {0};char path[2048] = {0};char decodePath[1024] = {0};char protocol[128] = {0};//sscanf解析格式化字符串sscanf(line, "%[^ ] %[^ ] %s", method, path, protocol);printf("method: %s, path: %s protocol: %s\n", method, path, protocol);if (0 != strcasecmp(method, "get")) {return -1;}urlDecode(path, decodePath);//http中/代表服务端工作的资源根目录char *file = NULL;if (0 == strcmp(decodePath, "/")) {file = ".";}else {file = decodePath + 1;}struct stat st;int ret = stat(file, &st);if (-1 == ret) {//回复404页面sendHeadMsg(cfd, 404, "Not Found", getFileType(".html"), -1); //-1表示不知道长度,让浏览器自己解析去sendFile(cfd, "404.html");return 0;}if (S_ISDIR(st.st_mode)) {sendHeadMsg(cfd, 200, "OK", getFileType(".html"), -1);sendDir(cfd, file);}else {sendHeadMsg(cfd, 200, "OK", getFileType(file), st.st_size);sendFile(cfd, file);}return 0;
}int sendFile(int cfd, const char *fileName) {//读一部分数据,发送一部分数据,因为tcp是面向连接的流式的int fd = open(fileName, O_RDONLY);assert(fd > 0);
#if 0while (1) {char buf[1024];int len = read(fd, buf, sizeof(buf));if (len > 0) {send(cfd, buf, len, 0);usleep(20); //减轻接收端压力}else if (0 == len) {break;}else {perror("read");}}
#endif
#if 1off_t len = 0;int size = lseek(fd, 0, SEEK_END);lseek(fd, 0, SEEK_SET);while (len < size) {int ret = sendfile(cfd, fd, &len, size - len);printf("ret value %d \n", ret);if (-1 == ret) {if (EAGAIN == errno) {printf("no data\n");perror("sendfile");}else {printf("client quit \n");break;}}}
#endifreturn 0;
}int sendHeadMsg(int cfd, int status, const char *descr, const char *type, int length) {char buf[8192] = {0};int offset = 0;int ret = sprintf(buf + offset, "http/1.1 %d %s\r\n", status, descr);offset += ret;ret = sprintf(buf + offset, "content-type: %s\r\n", type);offset += ret;ret = sprintf(buf + offset, "content-length: %d\r\n\r\n", length);offset += ret;send(cfd, buf, offset, 0);return 0;
}const char *getFileType(const char *name) {const char* dot = strrchr(name, '.');if (NULL == dot) {return "text/palin; charset=utf-8";}if (0 == strcasecmp(dot, ".html")) {return "text/html; charset=utf-8";}if (0 == strcasecmp(dot, ".png")) {return "image/png; charset=utf-8";}if (0 == strcasecmp(dot, ".txt")) {return "text/palin; charset=utf-8";}// ...return "application/octet-stream; charset=utf-8";
}int sendDir(int cfd, const char*dirName) {char buf[2048] = {0};int len = 0;int ret = sprintf(buf + len, "<html><head><title>%s</title><body><table>", dirName);len += ret;struct dirent** namelist = NULL;int num = scandir(dirName, &namelist, NULL, alphasort);for (int i = 0; i < num; i++) {char * name = namelist[i]->d_name;struct stat st;char path[1024] = {0};sprintf(path, "%s/%s", dirName, name);stat(path, &st);if (S_ISDIR(st.st_mode)) {if (!strcmp(".", name) || !strcmp("..", name)) {continue;}ret = sprintf(buf + len, "<tr><td><a href=\"%s/\" style=\"font-size:20px\">%s</a>    </td><td>%ld</td></tr>", \name, name, st.st_size);}else {ret = sprintf(buf + len, "<tr><td><a href=\"%s\" style=\"font-size:20px\">%s</a>    </td><td>%ld</td></tr>",name, name, st.st_size);}len += ret;send(cfd, buf, len, 0);len = 0;memset(buf, 0x00, sizeof(buf));free(namelist[i]);}len = sprintf(buf, "</table></head></body></html>");send(cfd, buf, len, 0);free(namelist);return 0;
}int hex2dec (char c) {if ('0' <= c && c <= '9') return c - '0';else if ('a' <= c && c <= 'f') return c - 'a' + 10;else if ('A' <= c && c <= 'F') return c - 'A' + 10;return 0;
}char dec2hex (short int c) {if (0 <= c && c <= 9) return c + '0';else if (10 <= c && c <= 15) return c + 'A' - 10;return 0;
}void urlDecode(char* org, char *obj) {if (!org || !obj) {return;}//isxdigit:判断字符是否是十六进制字符for (; *org != '\0'; ++org, ++obj) {if ('%' == org[0] && isxdigit(org[1]) && isxdigit(org[2])) {*obj = hex2dec(org[1]) * 16 + hex2dec(org[2]);org += 2;}else {*obj = *org;}}
}

主程序:

#include <stdio.h>
#include <unistd.h>
#include "HttpServer.h"int main(int argc, char** argv) {unsigned short int port;if (argc < 3) {printf("program {port} {path}\n");return -1;}sscanf(argv[1], "%hu", &port);printf("begin start http server, listen %d ...\n", port);//修改进程的工作目录chdir(argv[2]);int lfd = initListenFd(port);epoolRun(lfd);return 0;
}

Makefile 编译脚本:

app: httpServer tcpServer tcpClient#说明:$^代表依赖项
httpServer: CommonUtil.c HttpServer.c main.cgcc -g $^ -o httpServer -lpthread tcpServer: CommonUtil.c TcpServer.cgcc -g $^ -o tcpServertcpClient: CommonUtil.c TcpClient.cgcc -g $^ -o tcpClientclean:-rm httpServer tcpServer tcpClient -f

2.2 效果

 展示web服务器的根目录:

在线浏览文件:

下载文件:

3.TCP服务器和客户端

tcp协议作用途广泛,它是面向连接的流式协议,三次握手建立连接,四次挥手断开连接,其通信流程如下:

3.1 源码

头文件:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>int isLittleEndian();int readn(int fd, char* buf, int len);int writen(int fd, char *buf, int len);

源文件

 #include <strings.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include "CommonUtil.h"static const char* _s_showMsg [] = {"次北固山下","【作者】王湾 【朝代】唐","客路青山外,行舟绿水前。","潮平两岸阔,风正一帆悬。","海日生残夜,江春入旧年。","乡书何处达?归雁洛阳边。"
};static void sendMsg(int fd, const char *data, int len) {char *buf = (char *)malloc(sizeof(int) + len);int nlen = len;if (isLittleEndian()) {nlen = htonl(len);}memcpy(buf, &nlen, sizeof(int));memcpy(buf + sizeof(int), data, len);printf("发送数据长度:: [%d] 内容:: [%s]\n", len, data);writen(fd, buf, sizeof(int) + len);if (buf) {free(buf);}
}static int startTcpServer(unsigned short port)
{int lfd = socket(AF_INET, SOCK_STREAM, 0);if (-1 == lfd) {perror("scoket");return -1;}int opt = -1;int ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));if (-1 == ret) {perror("setsockopt");return -1;}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = INADDR_ANY;ret = bind(lfd, (const struct sockaddr *)&addr, sizeof(addr));if (-1 == ret) {perror("bind");return -1;}ret = listen(lfd, 128);if (-1 == ret) {perror("listen");return -1;}struct sockaddr_in client;socklen_t client_addrlen = sizeof(client);printf("port %d, wait client accept ...\n", port);int connfd = accept(lfd, (struct sockaddr*)(&client), &client_addrlen);if (connfd < 0) {perror("accept");return -1;}printf("connect client info: addr = %s, port = %d\n",inet_ntoa(client.sin_addr), ntohs(client.sin_port));int lineNum = sizeof(_s_showMsg) / sizeof(_s_showMsg[0]);for (int i = 0; i < lineNum; i++) {sendMsg(connfd, _s_showMsg[i], strlen(_s_showMsg[i]));usleep(1000 * 100); //此处为了减轻客户端压力}printf("我活干完了,数据已经全部发送到客户端!\n");getchar();close(connfd);close(lfd);return 1;
}int main(int argc, char**argv) {if (argc < 2) {printf("a.out {port}\n");return -1;}unsigned short port;sscanf(argv[1], "%hu", &port);startTcpServer(port);return 0;
}
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include "CommonUtil.h"static void recvMsg(int fd, char **data, int *len) {int nlen = 0;int ret = readn(fd, (char*)&nlen, sizeof(int));*len = nlen;if (isLittleEndian()) {*len = ntohl(nlen);}char *tmp = (char *)malloc(*len + 1);readn(fd, tmp, *len);tmp[*len] = '\0';*data = tmp;
}static int startTcpClient(unsigned short port)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);    assert(-1 != sockfd);//指定服务器的ip和端口struct 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("127.0.0.1");inet_pton(sockfd, "127.0.0.1", &saddr.sin_addr.s_addr);//作为客户端不需要指定端口,系统自动给客户端设置端口,连接上后直接收发数据printf("begin connect port %d\n", port);int ret = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));   if (-1 == ret) {perror("connect");return 0;}while(1)    {               char *buff = NULL;          int ret = 0;recvMsg(sockfd, &buff, &ret);if (-1 == ret) {perror("read");break;}else if (0 == ret) {perror("server quit ");break;}else {if (buff) {printf("接收数据长度:: [%d] 内容:: [%s]\n", ret, buff);free(buff);}}printf("\r\n------------------------------\n\r");sleep(rand() % 10);}close(sockfd);    exit(0);
}int main(int argc, char**argv) {if (argc < 2) {printf("a.out {port}\n");return -1;}unsigned short port;sscanf(argv[1], "%hu", &port);startTcpClient(port);return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "CommonUtil.h"int isLittleEndian() {static unsigned short data = 0x1234;if (*((char*)&data) == 0x34) {return 1;}else {return 0;}
}int readn(int fd, char* buf, int len) {int nleft = len;int nread = 0;char *pbuf = buf;while (nleft > 0) {nread = read(fd, pbuf, nleft);if (-1 == nread) {perror("read");return -1;}else if (0 == nread) {return len - nleft;}pbuf += nread;nleft -= nread;}return len;
}int writen(int fd, char *buf, int len) {int nleft = len;int nwrite = 0;char *pbuf = buf;while (nleft > 0) {nwrite = write(fd, pbuf, nleft);if (-1 == nwrite) {perror("write");return -1;}else if (0 == nwrite) {continue;}pbuf += nwrite;nleft -= nwrite;}return len;
}

编译脚本:

app: httpServer tcpServer tcpClient#说明:$^代表依赖项
httpServer: CommonUtil.c HttpServer.c main.cgcc -g $^ -o httpServer -lpthread tcpServer: CommonUtil.c TcpServer.cgcc -g $^ -o tcpServertcpClient: CommonUtil.c TcpClient.cgcc -g $^ -o tcpClientclean:-rm httpServer tcpServer tcpClient -f

3.2 效果

源码下载路径如下:
https://download.csdn.net/download/hsy12342611/87183336

只有平时多接触底层编程才能体会到一些技术的本质,做技术不能被表面的虚幻所迷惑,要从本质上去理解一些东西,好的,今天就到这里了,该去休息了。

Linux下C语言实现HTTP文件服务器和TCP协议实现网络数据传输相关推荐

  1. Linux 应用程序 嵌汇编,Linux下C语言嵌汇编

    Using Assembly Language in Linux. Intel和AT&T汇编语法差异: 1.前缀: Intel汇编寄存器和立即数无需前缀.后者寄存器前缀为%,立即数前缀为$. ...

  2. linux c语言工具,Linux下C语言编程环境的工具.doc

    Linux下C语言编程环境的工具 Linux下C语言编程环境的工具 Linux下C语言编程环境的工具 要想在Linux下进行C语言编程,首先得搭建好一个编程环境.这里分别说明一下几个非常有用的软件包. ...

  3. Linux下C语言编程-进程的创建

    Linux下C语言编程-进程的创建 作者:hoyt 1.进程的概念 Linux操作系统是面向多用户的.在同一时间可以有许多用户向操作系统发出各种命令.那么操作系统是怎么实现多用户的环境呢?在现代的操作 ...

  4. linux父进程中显示子进程pid,请教linux下c语言函数fork父进程打印子进程的PID

    请教linux下c语言函数fork父进程打印子进程的PID 关注:296  答案:2  信息版本:手机版 解决时间 2019-01-14 04:55 雨不眠的下 2019-01-13 12:23 用于 ...

  5. 利用多线程实现linux下C语言的聊天室程序:

    转载:http://www.360doc.com/content/16/0421/11/478627_552531090.shtml 利用多线程实现linux下C语言的聊天室程序: 客户端代码: th ...

  6. linux+下c语言编程项目,精通UNIX下C语言编程与项目实践

    cc -I  //include 目录 -L //静态库目录?动态也可以 -l //小写L,接静态库名称?动态也可以 -DXXX='"XXFF"' //-D直接定义宏 -c 只编译 ...

  7. GCC编译器简明教程(Linux下C语言开发环境的搭建)

    GCC编译器简明教程(Linux下C语言开发环境的搭建) 市面上常见的Linux都是发行版本,典型的Linux发行版包含了Linux内核.桌面环境(例如GNOME.KDE.Unity等)和各种常用的必 ...

  8. 您知道Linux下C语言编程的一些注意事项吗_教育中国

    您知道Linux下C语言编程的一些注意事项吗_教育中国 云风的 BLOG: 一个 C 接口设计的问题 一个 C 接口设计的问题 C 语言在本质上,参数传递都是值传递.不像 Pascal 和 C++ 可 ...

  9. 半双工通信是指c语言,Linux下C语言实现半双工的UDP通信

    ------------恢复内容开始------------ Linux下C语言实现半双工的UDP通信 1.单向通信:又称为单工通信,即只能有一个方向的通信而没有反方向的交互.无线电广播或有线电广播以 ...

最新文章

  1. linux运维、架构之路-MySQL多实例
  2. 用.NET SqlBulkCopy类执行批量复制
  3. Python之pandas:数据类型变换之object、category、bool、int32、int64、float64以及数据类型标准化之详细攻略
  4. oracle 填入编号,sql – 带填充模式的Oracle to_char格式编号(FM0000)
  5. mysql 5.7.6 5.7.19_MySQL数据库之Mysql 5.7.19 免安装版遇到的坑(收藏)
  6. AUTOSAR从入门到精通100讲(三十六)-AUTOSAR 通信服务两步走-CanSM概念-配置及代码分析
  7. 12c集群日志位置_Kubernetes(k8s)那些套路之日志收集
  8. vuex语法精简(方便开发查阅)
  9. spring cloud config的bootstrap.yml与application.proterties的区别
  10. (9)机器学习_多分类器OneVsRestClassifier
  11. asp.net银行账目管理系统VS开发sqlserver数据库web结构c#编程计算机网页源码项目
  12. 免费数据集下载(很全面)
  13. 雪中悍刀行热门数据读取
  14. android卡通头像,Face V(卡通头像制作)
  15. zzuli:1000从今天开始入坑C语言
  16. mysql 学习指南
  17. 计算机无法对NAS硬盘操作,群晖NAS联机失败不要慌,我用经验告诉你,这样做就能完美解决...
  18. 在西安,1000万人的城市,有多少家IT培训机构?
  19. WebDAV之葫芦儿·派盘+FX文件管理器
  20. 如何关闭135端口、139端口、445端口

热门文章

  1. 微信公众号服务器配置 --接口 nodejs实现方法
  2. 九月九深圳宝安新馆!ELEXCON电子展等你踏浪而来~
  3. 我是谁:没有绝对安全的系统影评
  4. 简述ip地址的abc类如何划分_IP地址如何分类 ?ABC类IP是怎么划分的?
  5. 音频编解码(软/硬编码),音频转码
  6. Mobicents JAIN SLEE
  7. 双语:Threads 操作系统线程
  8. ZGC的多重映射,会不会造成 虚拟内存很多,而实际的物理内存已经不够用的情况?
  9. 华为S5700交换机初始化和配置SSH和TELNET远程登录方法
  10. 日加满借盛大游戏之力锁定游戏玩家