最简版本

简介: 最简版本

最简单的 TCP 服务器仅仅需要基于 sys/socket 库即可,实现如下:

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.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>
#define SERVER_PORT 6666
/*
监听后,一直处于accept阻塞状态,
直到有客户端连接,
当客户端如数quit后,断开与客户端的连接
*/
int main() {
  //调用socket函数返回的文件描述符
  int serverSocket;
  //声明两个套接字sockaddr_in结构体变量,分别表示客户端和服务器
  struct sockaddr_in server_addr;
  struct sockaddr_in clientAddr;
  int addr_len = sizeof(clientAddr);
  int client;
  char buffer[200];
  int iDataNum;
  // socket函数,失败返回-1
  // int socket(int domain, int type, int protocol);
  //第一个参数表示使用的地址类型,一般都是ipv4,AF_INET
  //第二个参数表示套接字类型:tcp:面向连接的稳定数据传输SOCK_STREAM
  //第三个参数设置为0
  if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
    perror("socket");
    return 1;
  }
  bzero(&server_addr, sizeof(server_addr));
  //初始化服务器端的套接字,并用htons和htonl将端口和地址转成网络字节序
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(SERVER_PORT);
  // ip可是是本服务器的ip,也可以用宏INADDR_ANY代替,代表0.0.0.0,表明所有地址
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  //对于bind,accept之类的函数,里面套接字参数都是需要强制转换成(struct sockaddr
  //*)
  // bind三个参数:服务器端的套接字的文件描述符,
  if (bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) <
      0) {
    perror("connect");
    return 1;
  }
  //设置服务器上的socket为监听状态
  if (listen(serverSocket, 5) < 0) {
    perror("listen");
    return 1;
  }
  while (1) {
    printf("监听端口: %d\n", SERVER_PORT);
    //调用accept函数后,会进入阻塞状态
    // accept返回一个套接字的文件描述符,这样服务器端便有两个套接字的文件描述符,
    // serverSocket和client。
    // serverSocket仍然继续在监听状态,client则负责接收和发送数据
    // clientAddr是一个传出参数,accept返回时,传出客户端的地址和端口号
    // addr_len是一个传入-传出参数,传入的是调用者提供的缓冲区的clientAddr的长度,以避免缓冲区溢出。
    //传出的是客户端地址结构体的实际长度。
    //出错返回-1
    client = accept(serverSocket, (struct sockaddr *)&clientAddr,
                    (socklen_t *)&addr_len);
    if (client < 0) {
      perror("accept");
      continue;
    }
    printf("等待消息...\n");
    // inet_ntoa ip地址转换函数,将网络字节序IP转换为点分十进制IP
    //表达式:char *inet_ntoa (struct in_addr);
    printf("IP is %s\n", inet_ntoa(clientAddr.sin_addr));
    printf("Port is %d\n", htons(clientAddr.sin_port));
    while (1) {
      printf("读取消息:");
      buffer[0] = '\0';
      iDataNum = recv(client, buffer, 1024, 0);
      if (iDataNum < 0) {
        perror("recv null");
        continue;
      }
      buffer[iDataNum] = '\0';
      if (strcmp(buffer, "quit") == 0)
        break;
      printf("%s\n", buffer);
      printf("发送消息:");
      scanf("%s", buffer);
      printf("\n");
      send(client, buffer, strlen(buffer), 0);
      if (strcmp(buffer, "quit") == 0)
        break;
    }
  }
  close(serverSocket);
  return 0;
}点击复制复制失败已复制


提示

配合客户端的最简版本一起学习使用。

目录
相关文章
|
存储 JSON 数据格式
Elasticsearch 8.X 可以按照数组下标取数据吗?
Elasticsearch 8.X 可以按照数组下标取数据吗?
|
Java
LogBack配置文件使用示例
LogBack配置文件使用示例 LogBack配置文件使用示例
114 0
7-5 sdut-C语言实验-最长公共子序列
7-5 sdut-C语言实验-最长公共子序列
211 0
|
编解码 并行计算 JavaScript
Nodejs 第十六章(ffmpeg)
Nodejs 第十六章(ffmpeg)
276 0
el-date-picker限制只能选当天,当天之后的日期(整理)
el-date-picker限制只能选当天,当天之后的日期(整理)
HarmonyOS学习路之开发篇—网络与连接(WLAN开发 二)
应用可以添加指定的热点,其选网优先级低于已保存热点。如果扫描后判断该热点为最合适热点,自动连接该热点。
|
算法 编译器 调度
程序并发操作中,解决数据同步的四种方法
程序并发操作中,解决数据同步的四种方法
321 0
程序并发操作中,解决数据同步的四种方法
|
监控 JavaScript
Vue3组件(三)把别人的组件拿过来再封装一下
在开源世界里面就是这么简单,我们完全可以在开源的UI库的基础上实现自己的想法。
488 0
Vue3组件(三)把别人的组件拿过来再封装一下
|
8天前
|
人工智能 运维 安全
|
5天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!