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

运行结果:

相关推荐
杨云龙UP1 小时前
SQL Server 备份异地同步 + 清理脚本
运维·服务器·数据库·sql·mysql·sqlserver
极地星光1 小时前
C++链式调用设计:打造优雅流式API
服务器·网络·c++
橘子真甜~2 小时前
C/C++ Linux网络编程8 - epoll + ET Reactor TCP服务器
linux·服务器·网络
万变不离其宗_82 小时前
centos 手动安装redis
linux·redis·centos
_lst_2 小时前
linux进程状态
linux·运维·服务器
稚辉君.MCA_P8_Java3 小时前
Gemini永久会员 归并排序(Merge Sort) 基于分治思想(Divide and Conquer)的高效排序算法
java·linux·算法·spring·排序算法
wanderist.3 小时前
Linux使用经验——离线运行python脚本
linux·网络·python
biter00884 小时前
Ubuntu 22.04 有线网络时好时坏?最终解决方案
linux·网络·ubuntu
德育处主任4 小时前
『NAS』轻松获取群晖自带的壁纸
服务器·docker