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;
}

运行结果:

相关推荐
期待のcode13 小时前
前后端分离项目 Springboot+vue 在云服务器上的部署
服务器·vue.js·spring boot
AI 智能服务13 小时前
第6课__本地工具调用(文件操作)
服务器·人工智能·windows·php
码农小韩14 小时前
基于Linux的C++学习——指针
linux·开发语言·c++·学习·算法
wdfk_prog14 小时前
[Linux]学习笔记系列 -- [fs]seq_file
linux·笔记·学习
Jay Chou why did15 小时前
wsl安装完无法进入wsl
linux
石头53016 小时前
Rocky Linux 9.6 docker k8s v1.23.17 kubeadm 高可用部署文档
linux
松涛和鸣16 小时前
49、智能电源箱项目技术栈解析
服务器·c语言·开发语言·http·html·php
凉、介16 小时前
SylixOS 中的 Unix Socket
服务器·c语言·笔记·学习·嵌入式·sylixos
RisunJan16 小时前
Linux命令-ipcs命令(报告进程间通信(IPC)设施状态的实用工具)
linux·运维·服务器