Linux系统编程——TCP服务器

Linux系统编程------TCP服务器

服务器流程

  1. 创建流式套接字,通过socket实现
  2. 填充服务器的网络信息结构体,struct sockaddr_in
  3. 将套接字与服务器的网络信息结构体绑定,通过bind实现
  4. 将套接字设置成被动监听状态,通过listen实现
  5. 阻塞等待客户端连接,通过accept实现
  6. 收发数据,通过recv/send实现
  7. 关闭套接字,通过close实现

socket函数

c 复制代码
int socket(int domain, int type, int protocol);

所需头文件:sys/socket.h

功能:创建socket

domain:协议族

type:socket类型

protocol:附件协议,无协议写0

返回值:成功返回套接字,失败返回-1并设置errno

domain取值

说明
AF_INET IPV4
AF_INET6 IPV6
AF_PACKET 原始套接字使用
AF_UNIX 本地通信
AF_LOCAL 本地通信

socket取值

说明
SOCK_STREAM TCP使用
SOCK_DGRAM UDP使用
SOCK_RAW 原始套接字使用

bind函数

c 复制代码
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);

所需头文件:sys/socket.h

功能:将套接字与服务器的网络信息结构体绑定

sockfd:文件描述符,由socket函数返回

addr:地址结构体指针,指向要绑定给sockfd的协议地址,结构体根据创建sock时的协议族不同而不同,这里以IPV4为例。

addrlen:地址结构体长度

返回值:成功返回0,失败返回-1并设置errno

sockaddr结构体

c 复制代码
struct sockaddr_in {
	sa_family_t sin_family; /*地址族*/
	in_port_t sin_port; //16位端口号
	struct in_addr sin_addr //32位IP地址
};

struct in_addr{
	uint32_t s_addr; //32位IPV4地址
};

listen函数

c 复制代码
int listen(int sockfd, int backlog);

所需头文件:sys/socket.h

功能:将套接字设置成被动监听状态

sockfd:要监听的套接字

backlog:连接队列的长度

返回值:成功返回0,失败返回-1并设置errno

accept函数

c 复制代码
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);

功能:阻塞等待客户端连接

sockfd:处于监听状态的socket

addr:用于保存客户端地址的结构体指针,如果不关系客户端信息,可以传NULL

addrlen:输入时为addr的缓冲区大小,输出时为实际地址长度,如果不关系客户端信息,可以传NULL。

返回值:成功返回新的socket文件描述符(由内核生成,代表着与返回客户端TCP连接,专用于与客户端通信),失败返回-1并设置errno

recv函数

c 复制代码
ssize_t recv(int sockfd, void* buf, size_t len, int flags);

所需头文件:sys/socket.h

功能:接收数据

sockfd:客户端的socket套接字

buf:存储接受的数据

len:数据大小

flags:控制选项(如MSG_DONTWAIT非阻塞,MSG_PEEK窥视数据, 0阻塞)

返回值:成功返回实际接受数据字节数,失败返回-1并设置errno

send函数

c 复制代码
ssize_t send(int sockfd, const void* buf, size_t len, int flags);

所需头文件:sys/socket.h

功能:发送数据

sockfd:客户端的sock套接字

buf:要发送数据的首地址

len:数据大小

flags:控制选项(如MSG_DONTWAIT非阻塞,MSG_PEEK窥视数据, 0阻塞)

返回值:成功返回实际发送数据字节数,失败返回-1并重置错误码

close函数

c 复制代码
int close(int fd);

所需头文件:unistd.h

fd:要关闭的套接字

返回值:成功返回0,失败返回-1

实例:实现一个TCP服务器

代码:

c 复制代码
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>

int main(int argc, char** argv) {
    //创建流式套接字
    struct sockaddr_in server_info;
        struct sockaddr_in client_info;
    socklen_t client_info_len = sizeof(client_info);
        int ret;
    int listen_fs = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_fs < 0) {
            perror("socket创建失败");
                return -1;
        }

        //配置服务器地址结构体
        //清空结构体
    memset(&server_info, 0, sizeof(server_info));
    memset(&client_info, 0, sizeof(client_info));
        //初始化IPV4协议族
        server_info.sin_family = AF_INET;
    //初始化端口号
    server_info.sin_port = htons(8888);
        //初始化IP
    server_info.sin_addr.s_addr = inet_addr("192.168.203.141");
        //绑定套接字与地址
    ret = bind(listen_fs,(struct sockaddr*)&server_info, sizeof(server_info));
        if (ret < 0) {
            perror("绑定失败");
                return -1;
        }
    //监听
        ret = listen(listen_fs, 128);
    if (ret < 0) {
            perror("监听失败");
                return -1;
        }

        //循环等待客户端连接
    while(true) {
            int connect_fd = accept(listen_fs,(struct sockaddr*)&client_info, client_info_len );
        if(connect_fd == -1) {
                    perror("等待连接失败");
                        return -1;
                }
                printf("客户端[%s:%d] 连接到服务器", inet_ntoa(client_info.sin_addr), ntohs(client_info.sin_port));
                //处理客户端通信(内部循环);
            while(true) {
                    char buf[128] = {0};
                        //int nbtyes = read(connect_fd, buf, sizeof(buf));
                        int nbytes = recv(connect_fd, buf, sizeof(buf), 0);
                        if (nbytes == -1) {
                            perror("读取失败");
                                return -1;
                        }

                        if (nbytes == 0) {
                            printf("客户端断开连接\n");
                                break;
                        }
                        printf("客户端[%s:%d]发来的数据:%s\n",inet_ntoa(client_info.sin_addr),ntohs(client_info.sin_port), buf);

                        //回显数据
                   int nbytes_send = send(connect_fd, buf, sizeof(buf), 0);
                   if (nbytes_send == -1) {
                      perror("发送失败");
                          return  -1;
                   }
                }
        }
        return 0;
}

运行结果:

相关推荐
JiMoKuangXiangQu21 小时前
ARM64 进程虚拟地址空间布局
linux·arm64 虚拟地址布局
阳光九叶草LXGZXJ1 天前
达梦数据库-学习-47-DmDrs控制台命令(LSN、启停、装载)
linux·运维·数据库·sql·学习
春日见1 天前
如何避免代码冲突,拉取分支
linux·人工智能·算法·机器学习·自动驾驶
珠海西格1 天前
“主动预防” vs “事后补救”:分布式光伏防逆流技术的代际革命,西格电力给出标准答案
大数据·运维·服务器·分布式·云计算·能源
无垠的广袤1 天前
【VisionFive 2 Lite 单板计算机】边缘AI视觉应用部署:缺陷检测
linux·人工智能·python·opencv·开发板
阿波罗尼亚1 天前
Kubectl 命令记录
linux·运维·服务器
一轮弯弯的明月1 天前
TCP传输大致过程、流量控制与拥塞控制
网络·tcp/ip·计算机网络·学习心得
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.1 天前
Keepalived单播模式配置与实战指南
linux·服务器·负载均衡
IDC02_FEIYA1 天前
Linux文件搜索命令有哪些?Linux常用命令之文件搜索命令find详解
linux·运维·服务器
江畔何人初1 天前
kubectl apply与kubectl create的区别
linux·运维·云原生