服务端编程初体验

1. 服务端客户端编程模式

  • 服务端长期暴露于网络,被动等待客户端的连接
  • 客户端则发起连接动作,等待服务端做出相应
  • 特点:
    • 服务端无法自己主动连接客户端
    • 客户端只能按照预定义的方式连接客户端

2. TCP的服务端程序实现步骤

  1. 创建套接字

  2. bind函数向套接字分配地址以及端口

    函数说明:

    #include <sys/socket.h>
    
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
    

    返回值:成功 0,否则 -1

    参数说明:

    • sockfd 待分配ip和端口的套接字文件描述符
    • addr 地址端口信息的结构体
    • addrlen 地址结构体变量长度
  3. 等待请求连接

    调用listen函数后,套接字进入等待连接请求。只有调用了listen函数,客户端才能进入可发出连接请求状态,即这时客户端才能调用connect函数(若提前调用将发生错误问题)。

    函数原型:

    #include <sys/types.h>          /* See NOTES */
    #include <sys/socket.h>
    
    int listen(int sockfd, int backlog);
    
    

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

    参数说明:

    • sockfd:用于监听的套接字
    • backlog:连接请求等待队列的长度,如,该值5,表示最多使5个连接请求进入等待连接的队列

    先解释一下等待连接请求状态的含义和连接请求等待队列含义,服务端处于等待连接请求状态是指,客户端请求连接时受理连接前一直处于请求等待状态

在这里插入图片描述

由图可知listen函数第一个参数的作用:可以理解成服务器端用于接收连接请求的一个门卫,当客户端有新连接请求时,服务端套接字就会将客户端的连接请求数据"请到"连接请求等候室,listen函数第二个参数就是这个等候室的大小,等候室被称为连接请求队列。该参数的大小与服务器的性能有联系。

  1. 处理/受理客户端连接请求

    调用listen函数后,如果有新的连接请求,则应按序受理。受理请求即进入可接受数据状态。数据通讯要使用套接字来完成,服务端已经有一个“门卫”套接字,但是这个套接字不能用于数据交换,不然谁来守门?所以服务器端要创建一个新的套接字与客户端套接字进行数据交换。

    accept函数用于创建与客户端套接字连接的套接字。

    函数原型:

    #include <sys/types.h>          /* See NOTES */
           #include <sys/socket.h>
    
           int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
    

    返回值:成功:文件描述符,失败:-1

    参数说明:

    • sockfd: 服务端套接字文件描述符
    • addr:用于保存发起连接请求的客户端地址信息,调用accept函数后,该值会被自动进行填充
    • addrlen:第二个结构体的长度

    accept函数调用成功后,内部将会自动创建一个用于和客户端套接字连接的套接字,并返回其文件描述符,并自动与发起连接的客户端建立对应的连接。

  2. 完成数据交换

  3. 断开套接字

在这里插入图片描述

3. 实例

实验目的:测试socket数据交换,listen 8899端口,使用网络调试工具模拟client,与服务端程序进行数据读写,client发送数据到服务端,当服务端接收的数据量大于16字节时,服务端向客户端发送"Hello world!".

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

int main(int argc ,char* argv[])
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if( server == -1 )
    {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8899);

    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
    {
        printf("server bind error\n");
        return -1;
    }

    if( listen(server, 1) == -1 )
    {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    asize = sizeof(caddr);

    client = accept(server, (struct sockaddr*)&caddr, &asize);

    if( client == -1 )
    {
        printf("client accept error\n");
        return -1;
    }

    printf("client: %d\n", client);

    len = 0;

    do
    {
        int i = 0;

        r = recv(client, buf, sizeof(buf), 0);

        if( r > 0 )
        {
            len += r;
        }
        
        printf("recv %s", buf);
        

    } while ( len <16 );

    printf("\n");

    send(client, "Hello world!", 12, 0);

    sleep(1);

    close(client);
    close(server);

    return 0;
}
/*
./a.out 
server start success
client: 4
recv 0abcde
recv 1abcde

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值