Skip to content

Linux 应用开发

文件和IO

  • 文件
    • 类型
      • 普通 目录d 字符设备c 块设备b 管道p 套接字s 符号链接l
      • ls -l
      • stat() fstat() lstat 文件信息
    • 符号链接
      • <unistd.h>
      • unlink() link() 创建移除硬链接
      • symlink() readlink() 创建和读取软链接
    • 权限
      • chown() lchown() fchown() 改变属主
      • chmod() fchmod() 改文件权限
    • 长度和截取
      • 空洞文件
      • truncate() ftruncate() 截取文件
    • 更改文件名 rename()
    • 文件时间戳 utime() utimes() futimes() lutimes()
  • 目录
    • mkdir() rmdir() 创建删除
    • opendir() readdir() 打开和读取
    • dirname() basename() 路径分解
  • 文件系统
    • 磁盘~(ntfs ext4)
    • 网络~(nfs samba)
    • 虚拟~(vfs)
  • IO
    • 标准io相比文件io多了缓存区以减少系统调用次数
    • 实践上一般用fopen打开普通文件,open打开设备文件
    • FILE* 流指针
      • 打开 fopen() fdopen() freopen()
      • 预定义~ 标准输入(键盘)输出(终端) 错误输出(终端)(无缓存)
      • fclose() 程序结束时自动关闭
      • FILE *fp; if((fp=fopen("txt","w"))==NULL){} fclose(fp);
    • 错误输出 perror("message") strerror(errno) errno是系统自带错误码变量
    • 流的读写
      • fputc() fgetc() fputs(char*,FILE*) 字符读写
      • fgets(char*, int size, FILE*) 字符串读写
      • fwrite() fread() 二进制(传指针)读写
    • 从输入输出获取
      • while((ch=fgetc(stdin))!=EOF){fputc(ch,stdout)} getchar() putchar()
      • while(fgets(buf,size,stdin)!=NULL){fputs(buf,stdout)} gets() puts()
    • 缓存
      • 全缓(文件)(4k) 行缓(标准输入输出)(1K) 不缓(错误输出)
      • 刷新条件 写满 fflush() 程序退出 \n
      • 更改全/行缓存 setbuf() setvbuf()
    • 流的定位 fseek() ftell() 去/查定位
    • 格式化输入输出
      • printf() fprintf() sprintf()
      • scanf() fscanf() sscanf()
      • 标准/文件/字符串
    • 文件IO
      • 打开关闭
        • int fd = open(path, flag, [mode]) 0<fd<1023
        • int close(int fd)
      • 读写
        • write(fd, (void *)buf, size)
        • read(fd, buf, size)
      • 定位 lseek(fd, offset, whence) 偏移后再写就会得到空洞文件
        • 上建议性锁 lockf()
        • 锁控制 int fcntl(fd, cmd, flock) flock结构体略
      • 生产消费模式实例
c
#include "fcntl.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"

const char* fifo_file = "./myfifo";
char buf[10];

int product() {
  int fd;
  unsigned int type, start, count, size;
  static unsigned int counter = 0;

  if ((fd = open(fifo_file, O_RDWR | O_CREAT | O_APPEND, 0664)) < 0) {
    perror("error");
    return 0;
  }
  type = 1;

  switch (type) {
    case 1:
      start = 'a';
      count = 26;
      break;
    case 2:
      start = '0';
      count = 10;
      break;
    default:
      return 0;
  }

  sprintf(buf, "%c", (start + counter));
  counter = (counter + 1) % count;
  lock_set(fd, F_UNLCK);
  close(fd);
  return 1;
}

int main() {
  int t = 1;
  int c = 10;
  while (c--) {
    if (!product()) break;
    sleep(t);
  }
  return 0;
}
c
#include "fcntl.h"
#include "lock_set.c"
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"

const long file_size = 100 * 1024 * 1024;
const char* fifo = "./myfifo";
const char* temp_file = "./tmp";

int customing(const char* myfifo, int need) {
  int fd;
  char buf;
  int cc = 0;
  if ((fd = open(myfifo, O_RDONLY)) < 0) {
    perror("error");
    return -1;
  }
  lseek(fd, SEEK_SET, 0);
  while (cc < need) {
    while ((read(fd, &buf, 1) == 1) && (cc < need)) {
      fputc(buf, stdout);
      cc++;
    }
  }
  fputs("\n", stdout);
  close(fd);
  return 0;
}

int myfilecopy(const char* sourse,
               const char* dest,
               int offset,
               int count,
               int mode) {
  int in, out;
  int counter = 0;
  char buff;

  if ((in = open(sourse, O_RDONLY)) < 0) {
    perror("error");
    return -1;
  }
  if ((out = open(dest, O_RDWR | O_CREAT | O_TRUNC, 0664)) < 0) {
    perror("error");
    return -1;
  }
  lseek(in, offset, SEEK_SET);
  while ((read(in, &buff, 1) == 1) && (counter < count)) {
    write(out, &buff, 1);
    counter++;
  }
  close(in);
  close(out);
  return 0;
}

int custom(int need) {
  int fd;
  customing(fifo, need);
  if ((fd = open(fifo, O_RDWR)) < 0) {
    perror("error");
    return -1;
  }
  // 为了模拟所作的平移
  lock_set(fd, F_WRLCK);
  myfilecopy(fifo, temp_file, need, file_size, 0);
  myfilecopy(temp_file, fifo, 0, file_size, 0);
  lock_set(fd, F_UNLCK);
  unlink(temp_file);
  close(fd);
  return 0;
}

int main() {
  int customer = 10;
  if (customer > 0)
    custom(customer);
  return 0;
}
c
#include "fcntl.h"
#include "stdio.h"
#include "stdlib.h"
#include "sys/stat.h"
#include "sys/types.h"
#include "unistd.h"

int lock_set(int fd, int type) {
  struct flock lock;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  lock.l_type = type;
  lock.l_pid = -1;

  fcntl(fd, F_GETLK, &lock);
  lock.l_type = type;
  if ((fcntl(fd, F_SETLKW, &lock)) < 0) {
    return -1;
  }
  return 0;
}

进程线程

  • 进程
    • 状态
      • 运行态 睡眠态 停止态 僵尸态 消亡态
    • 进程组和会话组
    • 优先级
    • 调度
      • 时间片结束(RR) 自愿放弃 进程终止 优先级抢占
    • 虚拟内存
      • 布局递增 程序代码 初始化数据 BBS 堆 共享库 栈 命令行参数和环境变量
    • 创建 fork() vfork()
      • if(pid<0){error}else if(pid==0){子进程}else{父进程}
    • 进程id getppid()自身 getppid()父进程id
    • 执行另外的进程
      • execl(path,arg...)``execlp(file,arg...) execle(path,arg...,环境变量)
      • execv(path,[arg]) execvp(file,[arg]) execvpe(file,[arg],环境变量)
    • 终止进程 exit() _exit()
    • 孤儿进程 父进程先结束了 init进程成为义父
    • 僵尸进程 程序退出却没有释放资源 子进程退出父进程没退出时产生
    • (父)等待 wait(子进程退出时的return) waitpid(pid,status,0/WHOHANG)
    • 守护进程
      • 创建子进程,父进程退出 fork() exit(0)
      • 子进程创建新会话 setsid()
      • 改变当前工作目录 chdir(path)
      • 重设文件权限掩码 umask(0)
      • 关闭文件描述符号 close(num)
    • 系统日志 syslog 用于守护进程输出信息
      • openlog(name,option, facility) syslog(priority,string) closelog()
  • 线程 pthread.h
    • 创建 pthread_create()
    • 退出
      • 线程 return
      • 自己 pthread_exit()
      • 他人 pthread_cancel()
      • 任意线程 exit() 全终止
      • main函数 return 全终止
    • 等待 pthread_join(thread, rt) 等待指定线程终止接收返回状态
    • 分离
      • pthread_detach(thread) 不关心返回状态,线程自行终止和清理
      • pthread_attr_init/destroy(*attr) pthread_attr_setdetachstate()创建即分离
    • 取消
      • pthread_cancel(thread) 向进程发送取消请求
      • pthread_setcancelstate(state,oldState) 取消状态设置
      • pthread_setcanceltype() 设置~取消类型
      • pthread_testcancel() 为当前线程设置取消点
    • 通信 使用全局变量即可
    • 互斥锁
      • 创建和释放 pthread_mutex_init() pthread_mutex_destroy()
      • 上锁和解锁 pthread_mutex_lock() pthread_mutex_unlock()
      • pthread_mutex_trylock(mutex)若占用报错而不等待
      • pthread_mutex_timedlock(mutex,timeout) 设置等待时间
    • 死锁
      • 在线程中对同一互斥锁多次加锁
      • 多个线程对多个锁交叉使用
      • 持有互斥锁的线程被其他线程取消
      • pthread_cleanup_push/pop() 向清理函数栈添加和移除清理函数 以规避死锁
    • 互斥锁属性
      • pthread_mutexattr_getpshared/setpshared() 获得/设置互斥锁属性
        • 一个进程内两个线程间互斥(默认) 不同进程线程互斥
      • pthread_mutexattr_gettype/settype() 设置互斥锁类型
        • 标准~ 检错~ 递归~(recursive)(同一线程多次上锁)
    • 信号量 semaphore.h
      • 创建销毁 sem_init/destory(sem)
      • 减一 sem_wait(sem) sem_trywait(sem)不阻塞 sem_timewait(sem, timeout)时间限制
      • 加一 sem_post(sem)
      • 获取 sem_getvalue(sem, save)
    • 条件变量 pthread.h 类似通知
      • 创建销毁 pthread_cond_destroy/init(cond)
      • 唤醒 pthread_cond_broadcast/signal(cond) 广播和单播
      • 等待 pthread_cond_timedwait/wait(cond, mutex, [time])
      • 用法
        • 主函数 初始化条件变量和互斥锁 创建线程以及join 销毁所和条件变量
        • 一个线程 循环上锁解锁给通知
        • 一个线程 循环上锁 等通知运行解锁
    • 线程池
c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

// 定义互斥锁
pthread_mutex_t mutex;

// 全局变量
int counter = 0;

// 线程函数
void *thread_function(void *arg) {
    // 加锁
    pthread_mutex_lock(&mutex);

    // 对全局变量进行操作
    counter++;
    printf("Thread %ld: counter = %d\n", (long)arg, counter);

    // 解锁
    pthread_mutex_unlock(&mutex);

    return NULL;
}

int main() {
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 创建线程数组
    pthread_t threads[10];

    // 创建并启动线程
    for (long i = 0; i < 10; i++) {
        pthread_create(&threads[i], NULL, thread_function, (void *)i);
    }

    // 等待线程结束
    for (long i = 0; i < 10; i++) {
        pthread_join(threads[i], NULL);
    }

    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);

    return 0;
}
c
#ifndef _POOL_H
#define _POOL_H

#include "assert.h"
#include "pthread.h"
#include "stdio.h"
#include "stdlib.h"
#include "sys/types.h"
#include "unistd.h"

// struct worker等同于 Worker
typedef struct worker {
  void* (*process)(void* arg);
  void* arg;
  struct worker* next;
} Worker;

typedef struct {
  pthread_mutex_t lock;
  pthread_cond_t cond;
  Worker* head;
  int shutdown;
  pthread_t* tid;
  int max_num;
  int queue_size;
} Pool;

extern void pool_int(int max_num);
extern void* thread_routine(void* arg);
extern void* myprocess(void* arg);
extern int pool_add_worker(void* (*process)(void* arg), void* arg);
extern int pool_destroy();
#endif
c
#include "pool.h"

static Pool* pool = NULL;

void ppinfo() {
  printf("0x%x", pthread_self());
}

void* thread_routine(void* arg) {
  pp();
  while (1) {
    pthread_mutex_lock(&(pool->lock));
    // 队列空
    while (pool->queue_size == 0 && !pool->shutdown) {
      pp();
      pthread_cond_wait(&(pool->cond), &(pool->lock));
    }
    // 摧毁
    if (pool->shutdown) {
      pthread_mutex_unlock(&(pool->lock));
      pp();
      pthread_exit(NULL);
    }

    pp();
    assert(pool->queue_size != 0);
    assert(pool->head != NULL);

    pool->queue_size--;
    Worker* worker = pool->head;
    pool->head = worker->next;
    pthread_mutex_unlock(&(pool->lock));
    ((*worker->process))(worker->arg);

    free(worker);
    worker = NULL;
  }
  pthread_exit(NULL);
}

void pool_init(int max_num) {
  pool = (Pool*)malloc(sizeof(Pool));
  pthread_mutex_init(&(pool->lock), NULL);
  pthread_cond_init(&(pool->lock), NULL);

  pool->head = NULL;
  pool->max_num = max_num;
  pool->queue_size = 0;
  pool->shutdown = 0;
  pool->tid = (pthread_t*)malloc(max_num * sizeof(pthread_t));
  for (int i = 0; i < max_num; i++)
    pthread_create(&(pool->tid[i]), NULL, thread_routine, NULL);
}

// 测试
void* test_process(void* arg) {
  pp();
  sleep(1);
  return NULL;
}

// 添加
int pool_add_worker(void* (*process)(void* arg), void* arg) {
  Worker* worker = (Worker*)malloc(sizeof(Worker));
  worker->process = process;
  worker->arg = arg;
  worker->next = NULL;
  pthread_mutex_lock(&(pool->lock));
  Worker* member = pool->head;
  if (member != NULL) {
    while (member->next != NULL) {
      member = member->next;
    }
    member->next = worker;

  } else {
    pool->head = worker;
  }
  assert(pool->head != NULL);
  pool->queue_size++;
  pthread_mutex_unlock(&(pool->lock));
  pthread_cond_signal(&(pool->cond));
}

// 删除
int pool_destroy() {
  if (pool->shutdown)
    return -1;
  pool->shutdown = 1;
  pthread_cond_broadcast(&(pool->cond));

  for (int i = 0; i < pool->max_num; i++) {
    pthread_join(pool->tid[i], NULL);
  }
  free(pool->tid);

  Worker* head = NULL;
  while (pool->head != NULL) {
    head = pool->head;
    pool->head = pool->head->next;
    free(head);
  }

  pthread_mutex_destroy(&(pool->lock));
  pthread_cond_destroy(&(pool->cond));

  free(pool);
  pool = NULL;
  return 0;
}
c
#include "pool.h"

int main() {
  pool_init(3);
  sleep(1);

  int* working_num = (int*)malloc(sizeof(int) * 10);
  for (int i = 0; i < 10; i++) {
    working_num[i] = i;
    pool_add_worker(myprocess, &working_num[i]);

    sleep(5);
    pool_destroy();
    free(working_num);
    return 0;
  }
}

进程通信

  • 管道使用上类似文件 本质是内核空间内存区域
  • 无名管道 unistd.h
    • 创建 pipe(int pipefd[2]) pipefd[0/1] 分别为读端和写端
    • 只用于亲缘关系进程间通信
    • 单工 有固定的读写端
    • 读写采用 read/write 等文件io方法操作fd
    • 不支持 lseek() 进行定位
    • 读取后消除
    • 大小固定(64k),写满后阻塞(<4k)
    • 读端关闭时写入,管道破裂,进程退出 close(fd[0])
    • 写关闭时可读,无数据时读操作阻塞
  • 有名管道 sys/types.h sys/stat.h
    • 创建 mkfifo(name, 权限) 不应重复创建
      • if(mkfifoi("fifo",0664)<0){} if(errno==EEXIST)fd=open("fifo",O_RDWR)
    • 可以用终端创建 mkfifo name
    • 销毁 system("rm fifo")
    • fd = open("fifo", mode) 打开后使用
    • 可以用于不相关进程通信
    • 也是内核内存,不可 lseek()
    • 先进先出
    • 文件io方式使用
    • 无数据时读操作阻塞 空间已满时写操作阻塞
  • 信号 异步通信 类似中断 signal.h
    • 处理方式 忽略 捕捉默认
    • 信号名称 kill -l可以看到
      • SIGUSR1 等等可以自定义
    • 信号注册
      • typedef void (*sighandler_t)(int) typedef void (*)(int) sighandler_t
      • sighandler_t signal(sigName, sighandler_t handler) != SIG_ERR
      • 进阶 int sigaction(sigName, act, oldact) act是包含处理方式的sigaction结构体
    • 信号发送
      • 发给进程 int kill(pid, sigName)
      • 发给自己 raise(sigName)
    • 定时器信号 unistd.h
      • int alarm(seconds) 发出 SIGALRM信号 返回剩余时间或0
  • 消息队列 可以双向通信sys/ipc.h sys/msg.h
    • key_t ftok(path, 混淆) 生成key
    • int msgid = msgget(key, msgflg) 创建队列
      • IPC_CREAT IPC_EXCL 0664
    • msgsnd(msgid,msgbuf* msg, size, 是否阻塞(0/IPC_NOWAIT)) 消息发送
      • struct msgbuf{long mtype;char mtext[1];}
    • msgrcv(msgid, msg, size, 第n+1个消息, 0/IPC_NOWAIT) 接收消息
    • msgctl(msgid, cmd, buf) 消息控制
      • IPC_STAT 获取属性 IPC_SET 设置属性 IPC_RMID 删除消息队列
    • 终端 ipcs -q
  • 共享内存 sys/shm.h
    • 最为高效 需要搭配互斥机制使用
    • int shmid = shmget(key/IPC_PRIVATE, size, IPC_CREAT/IPC_EXCL/0664) 创建
    • shmat(shmid, addr(NULL), 0(读写)/SHM_RDONLY(仅读)) 映射到进程的虚拟地址空间
    • shmdt(addr) 断开映射关系
    • shmctl(shmid, cmd(IPC_STAT/IPC_SET/IPC_RMID), struct shmid_ds *buf) 控制
  • 信号灯 sys/sem.h
    • 创建 int semid = semget(key, nsems, IPC_CREAT/IPC_EXCL/0664)
    • 控制 semctl(semid, semnum, cmd, union semun)
      • ipcstat ipcset ipcrmid setval getval
    • PV操作 semop(semid, struct sembuf *sops, 操作信号量的个数)
      • struct sembuf{}
      • unsigned short sem_num 编号
      • short sem_op 1/-1 加减
      • short sem_flg 0/SEM_UNDO(进程退出时自动释放信号量)
c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

int main() {
  int fd;
  char buf[128] = "";
  if (mkfifo("fifo", 0664) < 0) {
    if (errno == EEXIST) {
      fd = open("fifo", O_RDWR);
      printf("%d\n", fd);
    }
  } else {
    fd = open("fifo", O_RDWR);
    printf("%d\n", fd);
  }

  while (1) {
    fgets(buf, 128, stdin);
    buf[strlen(buf) - 1] = '\0';
    write(fd, buf, strlen(buf));
    // read(fd, buf, 128);
    if (strncmp(buf, "quit", 4) == 0) {
      break;
    }
  }

  return 0;
}
c
#include "errno.h"
#include "signal.h"
#include "stdio.h"
#include "string.h"
#include "sys/ipc.h"
#include "sys/msg.h"
#include "sys/types.h"

#define SIZE sizeof(struct msgbuf) - sizeof(long)

struct msgbuf {
  long mtype;
  char buf[128];
};

int main() {
  key_t key;
  if ((key = ftok(".", 'a')) < 0) {
    perror("error");
    return -1;
  }
  int msgid;
  struct msgbuf msg_snd, msg_rcv;
  if ((msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0664)) < 0) {
    if (errno != EEXIST) {
      perror("error");
      return -1;
    } else {
      msgid = msgget(key, 0664);
    }
  }
  pid_t pid;
  pid = fork();
  if (pid < 0) {
    perror("error");
    return -1;
  } else if (pid) {
    while (1) {
      msgrcv(msgid, &msg_rcv, SIZE, 200, 0);
      if (strncmp(msg_rcv.buf, "quit", 4) == 0) {
        kill(pid, SIGKILL);
        goto err;
      }

      printf("%s\n", msg_rcv.buf);
    }
  } else {
    while (1) {
      msg_snd.mtype = 100;
      fgets(msg_snd.buf, 128, stdin);
      msg_snd.buf[strlen(msg_snd.buf) - 1] = '\0';
      msgsnd(msgid, &msg_snd, SIZE, 0);
      if (strncmp(msg_snd.buf, "quit", 4) == 0) {
        kill(pid, SIGKILL);
        break;
      }
    }
  }

  return 0;
err:
  msgctl(msgid, IPC_RMID, NULL);
}
c
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/types.h>

int main() {
  key_t key;
  if ((key = ftok(".", 'q')) < 0) {
    perror("error");
    return -1;
  }
  int shmid;
  char* shm = "";
  // 创建/打开共享内存
  if ((shmid = shmget(key, 512, IPC_CREAT | IPC_EXCL | 0664)) < 0) {
    if (errno != EEXIST) {
      perror("err");
      return -1;
    } else {
      shmid = shmget(key, 512, 0664);
    }
  }

  if ((shm = shmat(shmid, NULL, 0)) > 0) {
    printf("%p", shm);
  }

  int semid;
  int semun;
  // 创建/打开信号灯
  struct sembuf sem;
  semid = semget(key, 2, IPC_CREAT | IPC_EXCL | 0664);

  if (semid < 0) {
    if (errno != EEXIST) {
      perror("error");
      return -1;
    } else {
      semid = semget(key, 2, 0644);
    }
  } else {
    semun = 0;
    semctl(semid, 0, SETVAL, semun);
    semun = 1;
    semctl(semid, 1, SETVAL, semun);
  }

  // 读操作
  while (1) {
    sem.sem_num = 1;
    sem.sem_op = -1;
    sem.sem_flg = 0;
    semop(semid, &sem, 1);

    fgets(shm, 128, stdin);
    shm[strlen(shm) - 1] = '\0';

    sem.sem_num = 0;
    sem.sem_op = 1;
    sem.sem_flg = 0;
    semop(semid, &sem, 1);

    if (strncmp(shm, "quit", 4) == 0) {
      goto ERR;
    }
  }

  // 写操作
  while (1) {
    sem.sem_num = 0;
    sem.sem_op = -1;
    sem.sem_flg = 0;
    semop(semid, &sem, 1);
    if (strncmp(shm, "quit", 4) == 0) {
      goto ERR;
    }
    printf("%s", shm);
    sem.sem_num = 1;
    sem.sem_op = 1;
    sem.sem_flg = 0;
    semop(semid, &sem, 1);
  }

  return 0;
ERR:
  shmdt(shm);
  shmctl(shmid, IPC_RMID, NULL);
  semctl(semid, 0, IPC_RMID, NULL);
  semctl(semid, 1, IPC_RMID, NULL);
}

网络

  • 层级
    • 网络接口和物理层(以太网)
    • 网络层(IP)
    • 传输层(TCP UDP)
    • 应用层(Telnet FTP HTTP)
  • IP
    • in_addr_t inet_aton(ipstr) int inet_addr(ipstr, in_addr_t) inet_pton(ipv6) 十转二
    • char* inet_ntoa(in_addr_t) inet_ntop(ipv6) 二转十
  • 端口 2^16 0-65536 1-1023 系统 1024-49151 用户 49152-65536 私有
  • 字节序 主机往往采用小端存储 网络字节序采用大端存储
  • 套接字
    • 类型
      • 流式~ TCP
      • 数据包~ UDP
      • 原始~ IP 主要用于协议开发
    • TCP
      • 服务端 socket() bind() listen() accept() recv()/recvfrom() send/sendto() close()
      • 客户端 socket() [bind()] connect() send()/sendto() recv()/recvfrom() close()
    • UDP
      • 服务端 socket() bind() recvfrom() sendfrom()
      • 客户端 socket() sendto() recvfrom() close()
  • 数据包
    • 三握四挥
      • s socket() bind() listen()
      • c connect() 发送syn
      • s 发送ack syn
      • c ack
      • 通信
      • c 调用close() 发送fin
      • s 确认ack 然后close()发送fin
      • c 确认ack
    • 封装 解析
      • 以太网头部 IP头部 TCP/UDP头部 数据 以太网尾部
      • 封包格式 抓包 略
c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>

int main(int argc, char const* argv[]) {
  int sockfd, acceptfd;
  struct sockaddr_in serveraddr, clientaddr;
  socklen_t addrlen = sizeof(serveraddr);
  char buf[128] = {};
  bzero(&serveraddr, addrlen);
  bzero(&clearerr, addrlen);

  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    goto err;

  serveraddr.sin_family = AF_INET;
  serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
  serveraddr.sin_port = htons(atoi(argv[2]));

  if (bind(sockfd, (struct sockadd*)&serveraddr, addrlen) < 0)
    goto err;

  // udp不用listen和accept

  if (listen(sockfd, 5) < 0) {
    perror("error");
    return;
  }

  // 阻塞等待连接
  if ((acceptfd = accept(sockfd, &clientaddr, &addrlen)) < 0)
    goto err;

  printf("ip: %s,port: %d\n", inet_ntoa(clientaddr.sin_addr),
         ntohs(clientaddr.sin_port));

  int bytes;

  while (1) {
    if ((bytes = recv(acceptfd, buf, 128, 0)) < 0)
      goto err;
    else if (strncmp(buf, "quit", 4) == 0)
      goto close;
    else {
      printf("client: %s\n", buf);
      strcat(buf, "-server");
      if (send(acceptfd, buf, 128, 0) < 0)
        goto err;
    }
  }

  return 0;
err:
  perror("error");
  return -1;

close:
  close(acceptfd);
  close(sockfd);
  return 0;
}
c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>

int main(int argc, char const* argv[]) {
  int sockfd;
  struct sockaddr_in serveraddr;
  socklen_t addrlen = sizeof(serveraddr);
  char buf[128] = {};

  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    goto err;

  serveraddr.sin_family = AF_INET;
  serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
  serveraddr.sin_port = htons(atoi(argv[2]));

  // udp 不用connect

  if (connect(sockfd, &serveraddr, addrlen) < 0)
    goto err;

  while (1) {
    fgets(buf, 128, stdin);
    buf[strlen(buf) - 1] = '\0';

    if (send(sockfd, buf, 128, 0) < 0)
      goto err;

    if (strncmp(buf, "quit", 4) == 0)
      goto close;

    if (recv(sockfd, buf, 128, 0) < 0)
      goto err;

    printf("server: %s\n", buf);
  }

  return 0;
err:
  perror("error");
  return -1;
close:
  close(sockfd);
  return 0;
}

服务器

  • I/O模型
    • 阻塞 fgets() recvfrom()
    • 非阻塞
      • flags = fcntl(fd,F_GETFL)
      • flags = flags | O_NONBLOCK
      • fcntl(fd, F_SETFL, flags)
    • IO多路复用 unistd.h sys/select.h
      • 在一个程序中处理多个I/O流, 如果阻塞就会影响运行,如果非阻塞就会轮询浪费资源
      • select(fds, read, write, except, timeout) 监听文件描述符状态变化
      • FD_CLR() FD_ISSET() FD_SET() FD_ZERO() 删除 是否在 添加 清空
      • 信号驱动I/O略
      • poll() epoll()
      • select 数量有限制 开销大 遍历 水平触发
        • int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
        • void FD_CLR(int fd, fd_set *set);
        • int FD_ISSET(int fd, fd_set *set);
        • void FD_SET(int fd, fd_set *set);
        • void FD_ZERO(fd_set *set);
      • poll 链表保存⽂件描述符 没有数量限制 其它同上
      • epoll 使⽤⼀个⽂件描述符管理多个描述符 存放到内核的⼀个事件表 事件触发的,不是轮询查询的 效率高
  • 服务器模型
    • 循环服务器 依次处理每个客户端 不能同时多个
      • while(1){accept();while(1){recv();send();break;}}
    • 并发服务器
    • select并发
    • 文件服务器
    • udp聊天室
c
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 8080
#define MAX_CLIENTS 5

void* handle_client(void* arg) {
  int client_sock = *(int*)arg;
  char buffer[1024];

  while (1) {
    memset(buffer, 0, sizeof(buffer));
    int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
    if (bytes_received <= 0) {
      break;
    }

    printf("Received from client: %s\n", buffer);
    send(client_sock, buffer, bytes_received, 0);
  }

  close(client_sock);
  return NULL;
}

int main() {
  int server_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (server_sock == -1) {
    perror("Failed to create socket");
    exit(EXIT_FAILURE);
  }

  struct sockaddr_in server_addr;
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(PORT);
  server_addr.sin_addr.s_addr = INADDR_ANY;

  if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) ==
      -1) {
    perror("Failed to bind socket");
    exit(EXIT_FAILURE);
  }

  if (listen(server_sock, MAX_CLIENTS) == -1) {
    perror("Failed to listen on socket");
    exit(EXIT_FAILURE);
  }

  printf("Server listening on port %d...\n", PORT);

  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    int client_sock =
        accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
    if (client_sock == -1) {
      perror("Failed to accept client connection");
      continue;
    }

    printf("Accepted connection from client\n");

    pthread_t thread;
    if (pthread_create(&thread, NULL, handle_client, (void*)&client_sock) !=
        0) {
      perror("Failed to create thread");
      close(client_sock);
      continue;
    }

    pthread_detach(thread);
  }

  close(server_sock);
  return 0;
}
c
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>

#define PORT 8080
#define MAX_CLIENTS 5

void handle_client(int client_sock) {
  char buffer[1024];

  while (1) {
    memset(buffer, 0, sizeof(buffer));
    int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
    if (bytes_received <= 0) {
      break;
    }

    printf("Received from client: %s\n", buffer);
    send(client_sock, buffer, bytes_received, 0);
  }

  close(client_sock);
}

int main() {
  int server_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (server_sock == -1) {
    perror("Failed to create socket");
    exit(EXIT_FAILURE);
  }

  struct sockaddr_in server_addr;
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(PORT);
  server_addr.sin_addr.s_addr = INADDR_ANY;

  if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) ==
      -1) {
    perror("Failed to bind socket");
    exit(EXIT_FAILURE);
  }

  if (listen(server_sock, MAX_CLIENTS) == -1) {
    perror("Failed to listen on socket");
    exit(EXIT_FAILURE);
  }

  printf("Server listening on port %d...\n", PORT);

  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    int client_sock =
        accept(server_sock, (struct sockaddr*)&client_addr, &client_addr_len);
    if (client_sock == -1) {
      perror("Failed to accept client connection");
      continue;
    }

    printf("Accepted connection from client\n");

    pid_t pid = fork();
    if (pid == -1) {
      perror("Failed to fork process");
      close(client_sock);
      continue;
    } else if (pid > 0) {
      // Parent process
      close(client_sock);
      wait(NULL);  // Wait for child process to finish
    } else {
      // Child process
      handle_client(client_sock);
      exit(EXIT_SUCCESS);
    }
  }

  close(server_sock);
  return 0;
}
c
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 8080
#define MAX_CLIENTS 5

void handle_client(int client_sock) {
  char buffer[1024];

  while (1) {
    memset(buffer, 0, sizeof(buffer));
    int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
    if (bytes_received <= 0) {
      break;
    }

    printf("Received from client: %s\n", buffer);
    send(client_sock, buffer, bytes_received, 0);
  }

  close(client_sock);
}

int main() {
  int server_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (server_sock == -1) {
    perror("Failed to create socket");
    exit(EXIT_FAILURE);
  }

  struct sockaddr_in server_addr;
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(PORT);
  server_addr.sin_addr.s_addr = INADDR_ANY;

  if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) ==
      -1) {
    perror("Failed to bind socket");
    exit(EXIT_FAILURE);
  }

  if (listen(server_sock, MAX_CLIENTS) == -1) {
    perror("Failed to listen on socket");
    exit(EXIT_FAILURE);
  }

  printf("Server listening on port %d...\n", PORT);

  fd_set read_fds;
  FD_ZERO(&read_fds);
  FD_SET(server_sock, &read_fds);
  int max_fd = server_sock;

  while (1) {
    fd_set temp_fds = read_fds;
    int activity = select(max_fd + 1, &temp_fds, NULL, NULL, NULL);
    if (activity < 0) {
      perror("Error occurred in select()");
      exit(EXIT_FAILURE);
    }

    for (int i = 0; i <= max_fd && activity > 0; i++) {
      if (FD_ISSET(i, &temp_fds)) {
        if (i == server_sock) {
          // New client connection
          struct sockaddr_in client_addr;
          socklen_t client_addr_len = sizeof(client_addr);
          int client_sock = accept(server_sock, (struct sockaddr*)&client_addr,
                                   &client_addr_len);
          if (client_sock == -1) {
            perror("Failed to accept client connection");
            continue;
          }

          printf("Accepted connection from client\n");

          FD_SET(client_sock, &read_fds);
          if (client_sock > max_fd) {
            max_fd = client_sock;
          }
        } else {
          // Client message received
          handle_client(i);
          FD_CLR(i, &read_fds);
        }
        activity--;
      }
    }
  }

  close(server_sock);
  return 0;
}
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>

#define BUF_SIZE 1024
#define PORT 12345

int main() {
    int server_sock, client_sock;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_size;
    char buffer[BUF_SIZE];
    fd_set read_fds, temp_fds;
    int max_fd;

    // 创建套接字
    server_sock = socket(PF_INET, SOCK_STREAM, 0);
    if (server_sock == -1) {
        perror("socket");
        exit(1);
    }

    // 配置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);

    // 绑定套接字
    if (bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind");
        exit(1);
    }

    // 监听套接字
    if (listen(server_sock, 5) == -1) {
        perror("listen");
        exit(1);
    }

    // 初始化文件描述符集合
    FD_ZERO(&read_fds);
    FD_SET(server_sock, &read_fds);
    max_fd = server_sock;

    while (1) {
        temp_fds = read_fds;

        // 使用select检查文件描述符集合中的套接字状态
        if (select(max_fd + 1, &temp_fds, NULL, NULL, NULL) == -1) {
            perror("select");
            break;
        }

        // 遍历文件描述符集合,处理可读事件
        for (int i = 0; i <= max_fd; i++) {
            if (FD_ISSET(i, &temp_fds)) {
                if (i == server_sock) { // 有新的连接请求
                    client_addr_size = sizeof(client_addr);
                    client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &client_addr_size);
                    if (client_sock == -1) {
                        perror("accept");
                        break;
                    }
                    printf("New client connected: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
                    FD_SET(client_sock, &read_fds);
                    if (client_sock > max_fd) {
                        max_fd = client_sock;
                    }
                } else { // 有数据可读
                    int len = read(i, buffer, BUF_SIZE);
                    if (len <= 0) { // 客户端断开连接或出错
                        close(i);
                        FD_CLR(i, &read_fds);
                    } else { // 收到数据,发送回客户端
                        write(i, buffer, len);
                    }
                }
            }
        }
    }

    close(server_sock);
    return 0;
}
c
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 8080
#define MAX_CLIENTS 5

void handle_client(int client_sock) {
  char buffer[1024];

  while (1) {
    memset(buffer, 0, sizeof(buffer));
    int bytes_received = recv(client_sock, buffer, sizeof(buffer), 0);
    if (bytes_received <= 0) {
      break;
    }

    printf("Received from client: %s\n", buffer);
    send(client_sock, buffer, bytes_received, 0);
  }

  close(client_sock);
}

int main() {
  int server_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (server_sock == -1) {
    perror("Failed to create socket");
    exit(EXIT_FAILURE);
  }

  struct sockaddr_in server_addr;
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(PORT);
  server_addr.sin_addr.s_addr = INADDR_ANY;

  if (bind(server_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) ==
      -1) {
    perror("Failed to bind socket");
    exit(EXIT_FAILURE);
  }

  if (listen(server_sock, MAX_CLIENTS) == -1) {
    perror("Failed to listen on socket");
    exit(EXIT_FAILURE);
  }

  printf("Server listening on port %d...\n", PORT);

  int epoll_fd = epoll_create1(0);
  if (epoll_fd == -1) {
    perror("Failed to create epoll file descriptor");
    exit(EXIT_FAILURE);
  }

  struct epoll_event event;
  event.events = EPOLLIN;
  event.data.fd = server_sock;
  if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_sock, &event) == -1) {
    perror("Failed to add server socket to epoll");
    exit(EXIT_FAILURE);
  }

  while (1) {
    struct epoll_event events[MAX_CLIENTS];
    int num_events = epoll_wait(epoll_fd, events, MAX_CLIENTS, -1);
    if (num_events == -1) {
      perror("Error occurred in epoll_wait()");
      exit(EXIT_FAILURE);
    }

    for (int i = 0; i < num_events; i++) {
      if (events[i].data.fd == server_sock) {
        // New client connection
        struct sockaddr_in client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        int client_sock = accept(server_sock, (struct sockaddr*)&client_addr,
                                 &client_addr_len);
        if (client_sock == -1) {
          perror("Failed to accept client connection");
          continue;
        }

        printf("Accepted connection from client\n");

        event.events = EPOLLIN | EPOLLET;
        event.data.fd = client_sock;
        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_sock, &event) == -1) {
          perror("Failed to add client socket to epoll");
          close(client_sock);
          continue;
        }
      } else {
        // Client message received
        handle_client(events[i].data.fd);
        if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL) == -1) {
          perror("Failed to remove client socket from epoll");
        }
        close(events[i].data.fd);
      }
    }
  }

  close(server_sock);
  return 0;
}
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/epoll.h>

#define MAX_EVENTS 10
#define BUF_SIZE 1024

void setnonblocking(int sockfd) {
    int opts = fcntl(sockfd, F_GETFL);
    if (opts < 0) {
        perror("fcntl(F_GETFL)");
        exit(EXIT_FAILURE);
    }
    opts = (opts | O_NONBLOCK);
    if (fcntl(sockfd, F_SETFL, opts) < 0) {
        perror("fcntl(F_SETFL)");
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char *argv[]) {
    int server_sockfd, client_sockfd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len = sizeof(client_addr);
    char buffer[BUF_SIZE];

    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(8888);

    if (bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    if (listen(server_sockfd, 5) < 0) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    int epoll_fd = epoll_create1(0);
    if (epoll_fd == -1) {
        perror("epoll_create1");
        exit(EXIT_FAILURE);
    }

    struct epoll_event event, events[MAX_EVENTS];
    event.events = EPOLLIN;
    event.data.fd = server_sockfd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_sockfd, &event) == -1) {
        perror("epoll_ctl: server_sockfd");
        exit(EXIT_FAILURE);
    }

    while (1) {
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (nfds == -1) {
            perror("epoll_wait");
            exit(EXIT_FAILURE);
        }

        for (int i = 0; i < nfds; ++i) {
            if (events[i].data.fd == server_sockfd) {
                client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &client_addr_len);
                if (client_sockfd == -1) {
                    perror("accept");
                    exit(EXIT_FAILURE);
                }
                setnonblocking(client_sockfd);
                event.events = EPOLLIN | EPOLLET;
                event.data.fd = client_sockfd;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_sockfd, &event) == -1) {
                    perror("epoll_ctl: client_sockfd");
                    exit(EXIT_FAILURE);
                }
            } else {
                int done = 0;
                while (1) {
                    ssize_t count = read(events[i].data.fd, buffer, BUF_SIZE);
                    if (count == -1) {
                        if (errno != EAGAIN) {
                            perror("read");
                            done = 1;
                        }
                        break;
                    } else if (count == 0) {
                        done = 1;
                        break;
                    }
                    write(events[i].data.fd, buffer, count);
                }
                if (done) {
                    close(events[i].data.fd);
                }
            }
        }
    }

    close(server_sockfd);
    return 0;
}
c
#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

void list(int acceptfd) {
  DIR* dirp = opendir(".");
  struct dirent* dirent;
  char buf[128] = "";

  while (dirent = readdir(dirp)) {
    if (dirent->d_name[0] == '.')
      continue;
    else {
      strcpy(buf, dirent->d_name);
      send(acceptfd, buf, 128, 0);
    }
  }
  send(acceptfd, "OVER", 128, 0);
  return;
}

int download(int acceptfd, char* filename) {
  char buf[128] = "";
  int fd;
  int bytes;
  if ((fd = open(filename, O_RDONLY)) < 0) {
    if (errno == ENOENT) {
      strcpy(buf, "no exist");
      send(acceptfd, buf, 128, 0);
      return -1;
    } else
      perror("error");
  }

  strcpy(buf, "yes exist");
  send(acceptfd, buf, 128, 0);
  while ((bytes = read(fd, buf, 128)) > 0)
    send(acceptfd, buf, bytes, 0);

  sleep(1);
  strcpy(buf, "OVER");
  send(acceptfd, buf, 128, 0);
  return 0;
}

void upload(int acceptfd, char* filename) {
  int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0664);
  char buf[128] = "";
  int bytes;

  while ((bytes = recv(acceptfd, buf, 128, 0)) > 0) {
    if (strncmp(buf, "OVER", 8) == 0)
      break;
    write(fd, buf, bytes);
  }

  return;
}

int main() {
  int sockfd, acceptfd;
  struct sockaddr_in serveraddr, clientaddr;
  socklen_t addrlen = sizeof(serveraddr);
  char buf[128] = "";
  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    goto err;

  serveraddr.sin_family = AF_INET;
  serveraddr.sin_port = htons(8080);
  serveraddr.sin_addr.s_addr = INADDR_ANY;

  if ((bind(sockfd, &serveraddr, addrlen)) < 0)
    goto err;
  if ((listen(sockfd, 5)) < 0)
    goto err;

  while (1) {
    if ((acceptfd = accept(sockfd, &clientaddr, &addrlen)) < 0)
      goto err;
    int bytes;
    while (1) {
      if ((bytes = recv(acceptfd, buf, 128, 0)) < 0)
        goto err;
      else if (bytes) {
        printf("%s", buf);
        switch (buf[0]) {
          case 'L':
            list(acceptfd);
            break;
          case 'D':
            download(acceptfd, buf + 2);
            break;
          case 'P':
            upload(acceptfd, buf + 2);
            break;
        }
      } else
        break;
    }
  }
  close(acceptfd);
  close(sockfd);
  return 0;
err:
  perror("error");
  return -1;
}
c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

typedef struct {
  int type;
  char name[128];
  char text[128];
} MSG;

typedef struct node {
  struct sockaddr_in addr;
  struct node* next;
} link_t;

link_t* link_create() {
  link_t* h = (link_t*)malloc(sizeof(link_t));
  h->next = NULL;
  return h;
}

link_t* link_create();

void login(MSG msg, link_t* h, int sockfd, struct sockaddr_in clientaddr) {
  link_t* p = h;
  sprintf(msg.text, "---%s login---", msg.name);
  // 遍历链表
  while (p->next != NULL) {
    sendto(sockfd, &msg, sizeof(msg), 0, &p->next->addr,
           sizeof(struct sockaddr_in));
    p = p->next;
  }
  link_t* temp = (link_t*)malloc(sizeof(link_t));
  temp->addr = clientaddr;
  temp->next = h->next;
  h->next = temp;

  return;
}
void chat(MSG msg, link_t* h, int sockfd, struct sockaddr_in clientaddr) {
  char buf[128] = "";
  link_t* p = h;
  sprintf(buf, "%s :  %s", msg.name, msg.text);
  strcpy(msg.text, buf);

  while (p->next) {
    // 自己跳过
    if (memcmp(&clientaddr, &p->next->addr, sizeof(clientaddr)) == 0)
      p = p->next;
    else {
      sendto(sockfd, &msg, sizeof(msg), 0, &p->next->addr,
             sizeof(struct sockaddr_in));
      p = p->next;
    }
  }
  return;
}
void quitt(MSG msg, link_t* h, int sockfd, struct sockaddr_in clientaddr) {
  link_t* p = h;
  link_t* temp;

  sprintf(msg.text, "---%s offline ---", msg.name);

  while (p->next) {
    // 自己跳过
    if (memcmp(&clientaddr, &p->next->addr, sizeof(clientaddr)) == 0) {
      temp = p->next;
      p->next = temp->next;
      free(temp);
      temp = NULL;
    } else {
      sendto(sockfd, &msg, sizeof(msg), 0, &p->next->addr,
             sizeof(struct sockaddr_in));
      p = p->next;
    }
  }
  return;
}

int main() {
  int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  struct sockaddr_in serveraddr, clientaddr;
  socklen_t addrlen = sizeof(serveraddr);

  serveraddr.sin_family = AF_INET;
  serveraddr.sin_port = htons(8080);
  serveraddr.sin_addr.s_addr = INADDR_ANY;

  bind(sockfd, &serveraddr, addrlen);
  MSG msg;

  pid_t pid = fork();

  if (pid) {
    link_t* h = link_create();
    while (1) {
      recvfrom(sockfd, &msg, sizeof(msg), 0, &clientaddr, &addrlen);
      switch (msg.type) {
        case 1:
          login(msg, h, sockfd, clientaddr);
          break;
        case 2:
          chat(msg, h, sockfd, clientaddr);
          break;
        case 3:
          quitt(msg, h, sockfd, clientaddr);
          break;
      }
    }
  } else {
    // 子进程发送系统信息
    msg.type = 2;
    strcpy(msg.name, "server");
    while (1) {
      fgets(msg.text, 128, stdin);
      msg.text[strlen(msg.text) - 1] = '\0';
      sendto(sockfd, &msg, sizeof(msg), 0, &serveraddr, addrlen);
    }
  }

  return 0;
}

网络进阶

  • 超时检测
    • getsockopt() setsockopt()
    • select()
    • 定时器信号
  • 广播
    • ifconfig查看广播地址
    • sockfd = socket(AF_INET, SOCK_DGRAM, 0)
    • sendto(sockfd,buf,128,0,&broadcastaddr,addrlen)
  • 组播
    • struct ip_mreq mreq;
    • mreq.imr_multiaddr.s_addr = 224-239
    • mreq.imr_interface.s_addr = htonl(INADDR_ANY)
    • setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq))
  • Unix域套接字
    • 用于进程间通信 struct sockaddr_un
    • tcp
      • 服务端 创建 将sockfd和本地信息绑定 设置监听模式 接收连接请求 发送和接收
      • 客户端 创建 指定服务端 建立连接 发送和接收
    • udp
      • 服务端 创建 填充本地信息结构体 绑定套接字 发送和接收
      • 客户端 创建 填充双方本地信息 绑定 通信
  • 原始套接字
    • 用于读写ICMP IGMP等其他网络报文或自行构造ip头部和协议
    • 链路层创建 sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 需管理员
    • 网络层创建 通过setsockopt() 设置IP_HDRINCL 使得能自行构造ip头部
    • ICMP Ethernet 封包解析
    • MAC接收和发送
    • ARP实现MAC地址扫描 ARP是网络层和链路层之间的协议
      • 创建原始套接字
      • 组MAC头
      • 组ARP头
      • 将ARP请求通过eth0发送出去
      • 用ioctl()获取本机网络地址并设置sockaddr_ll
      • 接收数据
      • 分析ARP数据包
c
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>

int main() {
  int sockfd, acceptfd;
  struct sockaddr_un s_addr, c_addr;
  socklen_t addrlen = sizeof(s_addr);
  char buf[128] = "";

  bzero(&s_addr, addrlen);
  bzero(&c_addr, addrlen);

  sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
  // sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);

  // 和socket的主要区别
  s_addr.sun_family = AF_UNIX;
  strcpy(s_addr.sun_path, "./stream");

  bind(sockfd, &s_addr, addrlen);
  // udp不用
  listen(sockfd, 5);
  acceptfd = accept(sockfd, &c_addr, &addrlen);
  int r;
  while (1) {
    // udp用sockfd
    r = recv(acceptfd, buf, 128, 0);
    if (r) {
      if (strncmp(buf, "quit", 4) == 0) {
        printf("quit\n");
        break;
      } else {
        printf("client: %s\n", buf);
        strcat(buf, "--server");
        send(acceptfd, buf, 128, 0);
      }
    } else {
      printf("no data\n");
      exit(1);
    }
  }

  return 0;
}

应用

  • sqlite sqlite/sqlite3.h
    • sqlite3_open(filename,sqlite3) 创建
    • sqlite3_errmsg(sqlite3) 输出报错
    • sqlite3_close(sqlite3) 关闭
    • sqlite3_exec(sqlite3, sql_cmd, callback, arg, errmsg) 执行
    • sqlite3_get_table(sqlite3, sql_cmd, result, rows, columns,errmsg) 查询数据库
    • sqlite3_free_table(result) 释放查询结果
  • 管理系统