1, 网络字节序转换
#include <arpa/inet.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
// 使用无符号字符类型, 否则会越界.
unsigned char buf[4] = {192, 168, 1, 2};
printf("buf: {%d, %d, %d, %d}\n", *buf, *(buf+1), *(buf+2), *(buf+3));
// buf指针步长原本为1字节, 转为(unsigned int *),
// 步长变为4字节, 再通过*取值得到一个整型
unsigned int num = *(unsigned int *)buf;
printf("num: %d\n", num);
// 小端转大端字节序,即本机字节序转网络字节序
unsigned int sum = htonl(num);
unsigned char *p = (unsigned char *)∑
printf("p: {%d, %d, %d, %d}\n", *p, *(p+1), *(p+2), *(p+3));
// output: p: {2, 1, 168, 192} // 输出进行了翻转
// short占用两个字节,共16个比特位,能表示的数为2^16,总共65536.
unsigned short a = 0x0102;
unsigned short b = htons(a);
printf("b=%x\n", b); // C语言中%X的意思是以十六进制数形式输出整数
return 0;
}
2, 点分十进制串与网络大端数据互转
#include <arpa/inet.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
char buf[] = "192.168.1.4"; // 点分十进制串
unsigned int num = 0;
// 将点分十进制串转换为网络大端数据.
inet_pton(AF_INET, buf, &num);
unsigned char * p = (unsigned char *)#
printf("(%d, %d, %d, %d)\n", *p, *(p+1), *(p+2), *(p+3));
printf("sizeof num: %d\n", sizeof num);
// output: sizeof num: 4 // 得4个字节
char ip[16] = "";
// 网络大端数据转为点分十进制.
const char *resStr = inet_ntop(AF_INET, &num, ip, 16);
printf("res: (%s)", resStr);
return 0;
}
3,tcp客户端代码
#include <arpa/inet.h>
#include <stdio.h>
#include <cstdlib>
#include <sys/unistd.h>
#include <cstring>
int main(int argc, char *argv[]) {
//1, 创建套接字
int sock_fd;
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if(sock_fd == -1)
{
perror("socket");
exit(0);
}
//2, 连接服务器
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
// 点分十进制字符串 -> 网络大端整形数据
inet_pton(AF_INET, "192.168.1.101", &addr.sin_addr.s_addr);
int ret = connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
if(ret == -1)
{
perror("connect");
exit(0);
}
//3, 通过socket读写数据
while(1)
{
const char* pt = "你好, 服务器...\n";
// 发送数据
write(sock_fd, pt, strlen(pt)+1);
char buf[1024] = "";
int n = read(STDIN_FILENO, buf, sizeof(buf));
write(sock_fd, buf, n);
n = read(sock_fd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, n);
write(STDOUT_FILENO, "\n", 1);
}
// 4. 关闭文件描述符
close(sock_fd);
return 0;
}
4, tcp服务端单进程版
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/unistd.h>
#include <cstdlib>
#include <cstring>
int main(int argc, char *argv[]) {
// 1. 创建监听的套接字
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd == -1)
{
perror("socket");
exit(0);
}
// 2. 绑定
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.s_addr = INADDR_ANY; // 代表绑定的是一个通配地址.
// 绑定一个固定的地址
// inet_pton(AF_INET, "192.168.1.101", &addr.sin_addr.s_addr);
int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
if(ret == -1)
{
perror("bind");
exit(0);
}
// 3. 监听
ret = listen(lfd, 128);
if(ret == -1)
{
perror("listen");
exit(0);
}
// 4. 等待并接受客户端的连接
struct sockaddr_in addrcli;
int clilen = sizeof(addrcli);
int cfd = accept(lfd, (struct sockaddr*)&addrcli, &clilen);
char ipbuf[16];
printf("客户端的IP: %s, 端口: %d\n",
inet_ntop(AF_INET, &addrcli.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
ntohs(addrcli.sin_port));
if(cfd == -1)
{
perror("accept");
exit(0);
}
// 5. 通信
while(1)
{
// 收数据
char buf[1024];
memset(buf, 0, sizeof(buf));
// 这个函数是阻塞的
int len = read(cfd, buf, sizeof(buf));
printf("接收的数据, 客户端说: %s\n", buf);
if(len == 0) // 返回0,表示断开了连接
{
printf("客户端已经断开了连接...\n");
break;
}
// 回复数据
write(cfd, buf, len);
}
// 6. 关闭文件描述符
close(cfd);
close(lfd);
return 0;
}
5, tcp服务端单进程简版
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/unistd.h>
#include <cstring>
#include "lib/wrap.h"
/**
* 滑动窗口
* 1, mtu 1500 描述网卡最大发包字节长度
* 2, mss 1024 建立连接时会有(前两次会有,最大报文数据长度)
* 3, win 6144 描述本机窗口大小,即缓存区可接受的字节大小.
*
* @param argc
* @param argv
* @return
*/
int main(int argc, char *argv[]) {
int lfd = Tcp4bind(8001, NULL);
// 3. 监听
Listen(lfd, 128);
// 4. 等待并接受客户端的连接
struct sockaddr_in addrcli;
int clilen = sizeof(addrcli);
int cfd = Accept(lfd, (struct sockaddr*)&addrcli, &clilen);
char ipbuf[16];
printf("客户端的IP: %s, 端口: %d\n",
inet_ntop(AF_INET, &addrcli.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
ntohs(addrcli.sin_port));
// 5. 通信
while(1)
{
// 收数据
char buf[10240];
bzero(buf, sizeof(buf));
// 这个函数是阻塞的
int len = Readline(cfd, buf, sizeof(buf));
printf("接收的数据[%d]个, 客户端说: [%s]\n", len,buf);
if(len == 0) // 返回0,表示断开了连接
{
printf("客户端已经断开了连接...\n");
break;
}
// 回复数据
Write(cfd, buf, len);
}
// 6. 关闭文件描述符
Close(cfd);
Close(lfd);
return 0;
}
6, tcp服务端多进程版
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/unistd.h>
#include <cstring>
#include <cstdlib>
#include <sys/wait.h>
#include "lib/wrap.h"
/**
* 滑动窗口
* 1, mtu 1500 描述网卡最大发包字节长度
* 2, mss 1024 建立连接时会有(前两次会有,最大报文数据长度)
* 3, win 6144 描述本机窗口大小,即缓存区可接受的字节大小.
父进程阻塞在accept() 函数上等待客户端的连接,
突然客户端断开了连接, 服务器端的子进程随之退出,
服务器端的子进程给父进程发信号 SIGCHLD ,
在父进程中注册了信号捕捉, 当前信号产生之后, 父进程开始处理,
因为信号的优先级很高, 因此会中断进程原来的执行逻辑 ===>
父进程的accept() 阻塞被终断了
1. 处理信号 --> 回收子进程
2. 当前这一轮回收完毕, 退出回收函数
3. 程序执行回到被信号中断的位置 ==> accept()
1). 程序就不会继续阻塞了, 直接返回-1, 错误号 errno == EINTR
* @param argc
* @param argv
* @return
*/
int working_in_child(int cfd); // 子进程读写函数
void free_process(int sig); // 父进程注册的回收函数
int main(int argc, char *argv[]) {
// 0, 阻塞 SIGCHLD信号,
sigset_t set;
sigemptyset(&set); // 清空并初始化信号集
sigaddset(&set, SIGCHLD); // 添加一个信号到集合
// 进行信号阻塞操作, 确保该信号在注册回调函数后进行处理.
// 防止子进程提前退出未处理信号回调函数.
sigprocmask(SIG_BLOCK, &set, NULL);
// 1,创建监听套接字
int lfd = Tcp4bind(8001, NULL);
// 2. 监听
Listen(lfd, 128);
// 3, 循环等待并接受客户端的连接.
while (1) {
struct sockaddr_in addressCli;
int clientLen = sizeof(addressCli);
int cfd = Accept(lfd, (struct sockaddr*)&addressCli, &clientLen);
char stringIpBuf[16];
printf("客户端的IP: %s, 端口: %d\n",
inet_ntop(AF_INET, &addressCli.sin_addr.s_addr, stringIpBuf, sizeof(stringIpBuf)),
ntohs(addressCli.sin_port));
// 连接建立,进行通信
// 创建子进程, 使用子进程同客户端进行通信
pid_t pid = fork();
if (pid == 0) { // 这里为子进程
// 和客户端通信
// 关闭监听文件描述符
close(lfd);
working_in_child(cfd);
} else {
// 关闭读写文件描述符
close(cfd);
// 回收
// 注册信号回调
struct sigaction act;
act.sa_flags = 0;
act.sa_handler = free_process;
sigemptyset(&act.sa_mask); // 将信号集初始化并清空
sigaction(SIGCHLD, &act, NULL);
// 进行解除信号阻塞操作,后面才处理信号回调,
sigprocmask(SIG_UNBLOCK, &set, NULL);
}
}
return 0;
}
// 子进程与客户端通信的函数
int working_in_child(int cfd) {
while(1)
{
// 收数据
char buf[1024];
bzero(buf, sizeof(buf));
// 这个函数是阻塞的
int len = Read(cfd, buf, sizeof(buf));
printf("cfd: [%d], 接收的数据[%d]个, 客户端说: [%s]\n",cfd, len,buf);
if(len == 0) // 返回0,表示断开了连接
{
printf("客户端cfd: [%d]已经断开了连接...\n", cfd);
break;
}
// 回复数据
Write(cfd, buf, len);
}
// 6. 关闭文件描述符
Close(cfd);
exit(0);
return 0;
}
// 父进程回收子进程的函数
void free_process(int sig){
pid_t pid;
while (1) {
pid = waitpid(-1, NULL, WNOHANG);
if( pid <= 0 ) {
// 小于0 子进程全部退出了, 等于0 没有进程退出
break;
} else {
printf("处理子进程child pid = [%d]\n", pid);
}
}
}
7, tcp服务端多线程版
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/unistd.h>
#include <cstring>
#include <cstdlib>
#include <sys/wait.h>
#include <pthread.h>
#include "lib/wrap.h"
/**
* 滑动窗口
* 1, mtu 1500 描述网卡最大发包字节长度
* 2, mss 1024 建立连接时会有(前两次会有,最大报文数据长度)
* 3, win 6144 描述本机窗口大小,即缓存区可接受的字节大小.
父进程阻塞在accept() 函数上等待客户端的连接,
突然客户端断开了连接, 服务器端的子进程随之退出,
服务器端的子进程给父进程发信号 SIGCHLD , 在父进程中注册了信号捕捉, 当前信号产生之后,
父进程开始处理, 因为信号的优先级很高,
因此会中断进程原来的执行逻辑 ===> 父进程的accept() 阻塞被终断了
1. 处理信号 --> 回收子进程
2. 当前这一轮回收完毕, 退出回收函数
3. 程序执行回到被信号中断的位置 ==> accept()
1). 程序就不会继续阻塞了, 直接返回-1, 错误号 errno == EINTR
*/
typedef struct c_info
{
int cfd;
struct sockaddr_in clientAddress;
}CINFO;
void* working(void* arg);
int main(int argc, char *argv[]) {
/*if(argc < 2){
printf("argc < 2 ? , \n ./a.out 8001 \n");
return 0;
}
short port = atoi(argv[1]);*/
// 设置线程分离属性.
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 1,创建监听套接字
int lfd = Tcp4bind(8001, NULL);
// 2. 监听
Listen(lfd, 128);
CINFO *info;
while (1) {
struct sockaddr_in clientAddress;
socklen_t len = sizeof(clientAddress);
int cfd = Accept(lfd, (struct sockaddr*)&clientAddress, &len);
// 连接建立, 通信
// 子线程通信, 创建子线程
// 数据初始化
// 需要从数组中找一个空闲的元素, 初始化, 传给子线程
pthread_t pthreadId;
info = (CINFO *)malloc(sizeof(CINFO));
info->cfd = cfd;
info->clientAddress = clientAddress;
printf("\n\n\n客户端ip信息clientAddress->ptr: %p\n", &clientAddress);
printf("info->ptr: %p\n", info);
// attr 设置线程分离属性.
pthread_create(&pthreadId, &attr, working, info);
// 设置线程分离
// pthread_detach(pthreadId);
}
return 0;
}
// 子进程和客户端的通信函数
void* working(void* arg)
{
CINFO *info = (CINFO *)arg;
char stringIpBuf[16];
printf("working: cfd:[%d], 客户端的IP: %s, 端口: %d\n",
info->cfd,
inet_ntop(AF_INET, &(info->clientAddress.sin_addr.s_addr), stringIpBuf, sizeof(stringIpBuf)),
ntohs(info->clientAddress.sin_port));
while(1)
{
// 收数据
char buf[1024];
memset(buf, 0, sizeof(buf));
// 这个函数是阻塞的
int len = Read(info->cfd, buf, sizeof(buf));
if(len < 0) {
perror("数据读取有误");
break;
} else if(len == 0) {
printf("客户端已经断开了连接 -> cfd: %d\n", info->cfd);
break;
} else {
printf("接收的数据长度: [%d], 客户端说: %s\n", len, buf);
// 回复数据
write(info->cfd, buf, len);
}
}
close(info->cfd);
free(info);
return 0;
}
网友评论