用c语言编写简单的一对一服务器和多对一服务器

本文详细介绍了网络编程中的套接字功能,包括socket函数、地址族、套接字类型、协议转换、sockaddr_in结构、htons函数、inet_addr函数以及bind、listen、accept、send和connect等关键操作,展示了服务器端一对一和多对一的示例代码。

涉及到的一些函数:

socket

int socket(int domain, int type, int protocol);

//用于创建套接字,它是网络编程中的基础函数,用于建立网络通信。套接字是网络通信的端点,允许应用程序通过网络发送和接收数据。

//domain:表示套接字的地址族(Address Family)。常用的有 AF_INET(IPv4 地址族)和 AF_INET6(IPv6 地址族)。

//type:表示套接字的类型,常用的有 SOCK_STREAM(流套接字,用于TCP通信)和 SOCK_DGRAM(数据报套接字,用于UDP通信)等。

//protocol:表示使用的协议。通常设置为0,表示根据 domain 和 type 自动选择合适的协议。

socket函数返回一个整数值,如果创建套接字成功,它会返回一个非负的套接字描述符,用于后续的套接字操作。如果创建失败,返回 -1,表示出现了错误。

sockaddr_in

用于指定服务器地址、客户端地址

struct sockaddr_in {
    short sin_family;           // 地址族,通常设置为 AF_INET
    unsigned short sin_port;    // 端口号,需要使用 htons 函数转换为网络字节序
    struct in_addr sin_addr;    // IP地址,使用结构体 in_addr 表示
    char sin_zero[8];           // 保留字段,一般设置为 0
};

htons

在网络编程中,需要将数据在不同计算机之间进行传输,因此必须保证数据的字节序一致性。这就是 htons 函数的作用:将一个16位整数从主机字节序转换为网络字节序。具体来说,它将主机字节序的16位整数的字节顺序进行调整,以符合网络字节序的要求。

inet_addr

in_addr_t inet_addr(const char *cp);//用于将点分十进制字符串表示的IPv4地址转换为网络字节序的32位二进制整数表示的函数。

//cp: 是一个指向包含点分十进制字符串的指针,表示要转换的IPv4地址

bind

用于将一个套接字(socket)绑定到一个特定的地址和端口上,以便在这个地址和端口上监听传入的连接请求。在网络编程中,通常用于服务器端创建一个监听套接字,以便客户端能够连接到服务器。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

//sockfd:要绑定的套接字的文件描述符。

//addr:一个指向 struct sockaddr 结构体的指针,表示要绑定的地址信息。 //addrlen:addr 结构体的长度。

listen

用于将一个已绑定的套接字设置为监听模式,以便可以接受客户端的连接请求。它告诉操作系统,该套接字将用于接受传入的连接,而不是主动发起连接。

int listen(int sockfd, int backlog);

//sockfd:要监听的套接字的文件描述符,通常是一个已经通过 bind 绑定了地址和端口的套接字。

//backlog:定义了内核允许在套接字队列中排队等待的最大连接数。这个参数通常设置为一个合适的值,通常在 5 到 128 之间。

accept

用于接受传入的连接请求,并创建一个新的套接字来处理与客户端的通信。它通常在服务器中的主循环中使用,以等待客户端连接。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

//sockfd:表示已经通过 listen 函数设置为监听模式的套接字。

//addr:一个指向 struct sockaddr 结构体的指针,用于存储客户端的地址信息。 //addrlen:一个指向 socklen_t 类型的指针,用于指定 addr 结构体的大小。

accept 函数将会阻塞,直到有一个客户端连接请求到达为止。一旦连接请求到达,accept 函数将返回一个新的套接字文件描述符,该套接字表示与客户端的连接。同时,它会填充 addr 结构体以存储客户端的地址信息。

send

ssize_t send(int sockfd, const void *buf, size_t len, int flags);//用于将数据从一个已连接的套接字发送到另一个套接字。通常用于在客户端和服务器之间传输数据。

//sockfd:指定发送数据的套接字文件描述符。

//buf:一个指向要发送数据的缓冲区的指针。

//len:要发送的数据的长度,以字节为单位。

//flags:可选参数,通常可以设置为 0。

send 函数返回已发送的字节数,如果发生错误则返回 -1。在网络编程中,通常需要多次调用 send 函数来确保发送完整的数据。此函数通常是阻塞的,直到数据成功发送或发生错误。

connect

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//函数用于建立与远程主机的连接,通常在客户端程序中使用。

//sockfd:套接字文件描述符,指定要连接的本地套接字。

//addr:一个指向 sockaddr 结构体的指针,描述了远程服务器的地址和端口信息。

//addrlen:addr 结构体的大小。

连接成功,返回 0;如果出现错误,返回 -1。

recv

ssize_t recv(int sockfd, void *buf, size_t len, int flags);//用于从套接字接收数据

//sockfd:套接字文件描述符,指定从哪个套接字接收数据。 //buf:一个指向数据缓冲区的指针,接收到的数据将存储在这里。 //len:接收缓冲区的最大长度。 //flags:控制接收操作的标志位,通常设置为 0。

recv 函数的返回值是接收到的数据的字节数,如果出现错误,返回 -1。此函数可以阻塞等待数据,直到有数据到达,如果返回0,说明客户端关闭了。

服务器端代码(一对一)

监听指定端口(6000),并接受来自客户端的连接请求。一旦有客户端连接成功,它会接收客户端发送的数据,打印接收到的数据,并向客户端发送 "ok",然后关闭连接。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main() 
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) 
    {
        perror("socket creation failed");
        exit(1);
    }

    struct sockaddr_in saddr, caddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));
    if (res == -1)
    {
        perror("bind failed");
        exit(1);
    }

    res = listen(sockfd, 5);
    if (res == -1) 
    {
        perror("listen failed");
        exit(1);
    }

    while (1) 
    {
        int len = sizeof(caddr);
        int c = accept(sockfd, (struct sockaddr*)&caddr, &len);
        if (c < 0) 
        {
            perror("accept failed");
            continue;
        }

        printf("accept c=%d\n", c);

        char buff[128] = {0};
        int n;

        while ((n = recv(c, buff, sizeof(buff) - 1, 0)) > 0)
        {
            buff[n] = '\0';
            printf("Received: %s\n", buff);

            int sent_bytes = send(c, "ok", 2, 0);
            if (sent_bytes < 0)
            {
                perror("send failed");
                break;
            }

            if (strcmp(buff, "end\n") == 0)
            {
                close(c);
                close(sockfd);
                exit(0);
            }
        }

        close(c);
    }

    close(sockfd);
    return 0;
}

服务器端代码(多对一)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>

void * fun(void* arg)
{
    int *p = (int*)arg;
    int c = *p;
    free(p);
     while( 1 )
        {
            char buff[128] = {0};
            int n = recv(c,buff,127,0);
            if ( n <= 0)// recv返回值n == 0 说明客户但关闭连接了
            {
                break;
            }

            printf("recv=%s\n",buff);
            send(c,"ok",2,0);
        }

        close(c);//关闭连接  挥手
        printf("client close\n");
}
int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);//套接字 文件描述符
    if ( sockfd == -1 )
    {
        exit(1);
    }

    struct sockaddr_in saddr,caddr;//套接字地址 ip port 
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if ( res == -1 )
    {
        printf("bind err\n");
        exit(1);
    }

    res = listen(sockfd,5);
    if ( res == -1 )
    {
        exit(1);
    }

    while( 1 )
    {
        int c = accept(sockfd,NULL,NULL);
        if ( c < 0 )
        {
            continue;
        }

        printf("accept c=%d\n",c);

        pthread_t id;
        int * p = (int*)malloc(sizeof(c));
        *p = c;
        pthread_create(&id,NULL,fun,(void*)p);
    }
}

客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main() 
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) 
    {
        perror("Socket creation failed");
        exit(1);
    }

    struct sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));//connect表示三次握手开始
    if (res == -1) 
    {
        perror("Connect error");
        exit(1);
    }

    char buff[128] = {0};
    printf("Input (type 'end' to exit):\n");

    while (1) 
    {
        fgets(buff, 128, stdin);
        if (strcmp(buff, "end\n") == 0)
        {
            break;
        }
        send(sockfd, buff, strlen(buff) - 1, 0);
        memset(buff, 0, 128);
        recv(sockfd, buff, 127, 0);
        printf("Received: %s\n", buff);
    }

    close(sockfd);
    exit(0);
}

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值