前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >TCP服务器和客户端基础功能实现和要点解析

TCP服务器和客户端基础功能实现和要点解析

原创
作者头像
tankaro
修改2025-01-26 10:13:14
修改2025-01-26 10:13:14
980
举报
文章被收录于专栏:Linux 应用程序专栏

最简单TCP 服务器和客户端代码

重点理解

  1. tcp三次握手连接,四次握手断开;
  2. 服务器建立流程socket+bind+listen+accept+recv+send,有必须步骤,有非必须步骤,accept阻塞等待客户端connect。
  3. 服务器和客户端有一对一关系;

服务器代码

代码语言:txt
复制
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "tcp_define.h"

static int g_while_sleep_sencod = 2;

TCP_PACKET_DATA_T recv_data;
TCP_PACKET_DATA_T send_data;

int main(int argc, char *argv[])
{
    int sock_fd = -1;
    int accept_fd = -1;
    int ret = 0;
    struct sockaddr_in servaddr;
    char send_buf[MAX_BUFFER_SZIE] = "This buffer is in Server!!!!";
    uint64_t run_cnt = 0;
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    char *tcp_server_ip = (char *)TCP_SERVER_IP;
    uint16_t tcp_server_port = (uint16_t)TCP_SERVER_PORT;

    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sock_fd)
    {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(tcp_server_ip);
    servaddr.sin_port = htons(tcp_server_port);
    // bind是将socket绑定到某个IP和端口
    ret = bind(sock_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (-1 == ret)
    {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        goto err_bind_failed;
    }

    //设置同时与服务器建立连接的(监听)上限数,最大128个
    ret = listen(sock_fd, 2);
    if (-1 == ret)
    {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        goto err_listen_failed;
    }

    //这个版本只能用于多个客户端连接,而只有第一个客户端可以发送数据
    accept_fd = accept(sock_fd, (struct sockaddr *)&client, &len);
    if (-1 == accept_fd)
    {
        printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
        goto err_accept_failed;
    }
    printf("[%s +%d %s] tcp_server_port=%d\n", __FILE__, __LINE__, __func__, tcp_server_port);
    printf("[%s +%d %s] tcp_server_ip=%s\n", __FILE__, __LINE__, __func__, tcp_server_ip);

    printf("[%s +%d %s] then while recv+send\n", __FILE__, __LINE__, __func__);
    run_cnt = 0;
    while (1)
    {
        run_cnt ++;
        //阻塞等待第一个客户端的数据的到来,而且是为一个客户端数据的到来。
        memset(&recv_data, '\0', sizeof(TCP_PACKET_DATA_T));
        ret = recv(accept_fd, &recv_data, sizeof(TCP_PACKET_DATA_T), 0);
        printf("[%s +%d %s] recv ret=%d\n", __FILE__, __LINE__, __func__, ret);
        if (0 < ret)
        {
            printf("[%s +%d %s] [%ld] recv from client successful\n", __FILE__, __LINE__, __func__, run_cnt);
            printf("[%s +%d %s] recv_data.data=%s, recv_data.size=%d\n", __FILE__, __LINE__, __func__, recv_data.data, recv_data.size);
            printf("[%s +%d %s] recv_data.tcp_st=%d, recv_data.run_cnt=%ld\n", __FILE__, __LINE__, __func__, recv_data.tcp_st, recv_data.run_cnt);
        }
        else if (0 == ret)
        {
        }
        else
        {
        }

        strcpy(send_data.data, send_buf);
        send_data.size      = strlen(send_data.data);
        send_data.tcp_st    = TCP_STATUS_HANDSHAKE;
        send_data.run_cnt   = run_cnt;

        ret = send(accept_fd, &send_data, sizeof(TCP_PACKET_DATA_T), 0);
        printf("[%s +%d %s] send ret=%d\n", __FILE__, __LINE__, __func__, ret);
        if (0 < ret) {
            printf("[%s +%d %s] [%ld] send to client successful:\n", __FILE__, __LINE__, __func__, run_cnt);
            printf("[%s +%d %s] recv_data.data=%s, recv_data.size=%d\n", __FILE__, __LINE__, __func__, recv_data.data, recv_data.size);
            printf("[%s +%d %s] recv_data.tcp_st=%d, recv_data.run_cnt=%ld\n", __FILE__, __LINE__, __func__, recv_data.tcp_st, recv_data.run_cnt);
        }
        else if (0 == ret) {
        }
        else {
        }
        printf("[%s +%d %s] run_cnt=%ld then sleep(%d)\n", __FILE__, __LINE__, __func__, run_cnt, g_while_sleep_sencod);
        sleep(g_while_sleep_sencod);
    }
err_accept_failed:
err_listen_failed:
err_bind_failed:
    if(-1 != sock_fd)
    {
        close(sock_fd);
        sock_fd = -1;
    }
    return 0;
}

客户端代码

代码语言:txt
复制
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "tcp_define.h"

static int g_while_sleep_sencod = 2;

TCP_PACKET_DATA_T recv_data;
TCP_PACKET_DATA_T send_data;

int main(int argc, char *argv[])
{
    int sock_fd = -1;
    int ret = 0;
    struct sockaddr_in servaddr;
    char send_buf[MAX_BUFFER_SZIE] = "This buffer is in Client!!!!";
    uint64_t run_cnt = 0;
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    char *tcp_server_ip = (char *)TCP_SERVER_IP;
    uint16_t tcp_server_port = (uint16_t)TCP_SERVER_PORT;
    pid_t pid = 0;

    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sock_fd)
    {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    pid = getpid();
    printf("[%s +%d %s] PID pid=%d\n", __FILE__, __LINE__, __func__, pid);

    memset(&servaddr, 0, sizeof(struct sockaddr_in));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_port = htons(tcp_server_port);  
    servaddr.sin_addr.s_addr = inet_addr(tcp_server_ip); 
    printf("[%s +%d %s] tcp_server_port=%d\n", __FILE__, __LINE__, __func__, tcp_server_port);
    printf("[%s +%d %s] tcp_server_ip=%s\n", __FILE__, __LINE__, __func__, tcp_server_ip);

    ret = connect(sock_fd, (struct sockaddr*)&servaddr, len);
    while(0 != ret)
    {
        ret = connect(sock_fd, (struct sockaddr*)&servaddr, len);
        if(0 != ret)
        {
            break;
        }
        printf("[%s +%d %s] then sleep(1)\n", __FILE__, __LINE__, __func__);
    }
    printf("[%s +%d %s] then while send+recv\n", __FILE__, __LINE__, __func__);
    run_cnt = 0;
    while (1)
    {
        run_cnt ++;

        strcpy(send_data.data, send_buf);
        send_data.size      = strlen(send_data.data);
        send_data.tcp_st    = TCP_STATUS_HANDSHAKE;
        send_data.run_cnt   = run_cnt;

        ret = send(sock_fd, &send_data, sizeof(TCP_PACKET_DATA_T), 0);
        printf("[%s +%d %s] send ret=%d\n", __FILE__, __LINE__, __func__, ret);
        if (0 < ret) {
            printf("[%s +%d %s] [%ld] sned to client successful:\n", __FILE__, __LINE__, __func__, run_cnt);
            printf("[%s +%d %s] recv_data.data=%s, recv_data.size=%d\n", __FILE__, __LINE__, __func__, recv_data.data, recv_data.size);
            printf("[%s +%d %s] recv_data.tcp_st=%d, recv_data.run_cnt=%ld\n", __FILE__, __LINE__, __func__, recv_data.tcp_st, recv_data.run_cnt);
        }
        else if (0 == ret) {
        }
        else {
        }
        //阻塞等待第一个客户端的数据的到来,而且是为一个客户端数据的到来。
        memset(&recv_data, '\0', sizeof(TCP_PACKET_DATA_T));
        ret = recv(sock_fd, &recv_data, sizeof(TCP_PACKET_DATA_T), 0);
        printf("[%s +%d %s] recv ret=%d\n", __FILE__, __LINE__, __func__, ret);
        if (0 < ret)
        {
            printf("[%s +%d %s] [%ld] recv from client successful\n", __FILE__, __LINE__, __func__, run_cnt);
            printf("[%s +%d %s] recv_data.data=%s, recv_data.size=%d\n", __FILE__, __LINE__, __func__, recv_data.data, recv_data.size);
            printf("[%s +%d %s] recv_data.tcp_st=%d, recv_data.run_cnt=%ld\n", __FILE__, __LINE__, __func__, recv_data.tcp_st, recv_data.run_cnt);
        }
        else if (0 == ret)
        {
        }
        else
        {
        }

        printf("[%s +%d %s] run_cnt=%ld then sleep(%d)\n", __FILE__, __LINE__, __func__, run_cnt, g_while_sleep_sencod);
        sleep(g_while_sleep_sencod);
    }

    if(-1 != sock_fd)
    {
        close(sock_fd);
        sock_fd = -1;
    }
    return 0;
}

tcp_define.h代码

代码语言:txt
复制
#ifndef __TCP_DEFINE_H__
#define __TCP_DEFINE_H__
#include <stdint.h>

#define TCP_SERVER_PORT         (9999)  //tcp服务器端口
#define TCP_SERVER_IP           "192.168.70.64" //tcp服务端IP
#define MAX_BUFFER_SZIE         (4096) //接收,发送buffer 最大长度
#define MAX_PACKET_DATA_SIZE    (128)

typedef enum
{
    TCP_STATUS_NONE = 0,
    TCP_STATUS_HANDSHAKE,
    TCP_STATUS_HANDSHAKE_ACK,
    TCP_STATUS_ABNORMAL,  // Abnormal
    TCP_STATUS_ABNORMAL_ACK,
    TCP_STATUS_MAX
} TCP_STATUS_ST;

typedef struct __tcp_packet_data_ {
    char data[MAX_PACKET_DATA_SIZE];
    uint32_t size;
    TCP_STATUS_ST tcp_st;
    uint64_t run_cnt;
} TCP_PACKET_DATA_T;
#endif

服务器运行log

代码语言:txt
复制
[server.c +67 main] tcp_server_port=9999
[server.c +68 main] tcp_server_ip=192.168.70.64
[server.c +70 main] then while recv+send
[server.c +78 main] recv ret=144
[server.c +81 main] [1] recv from client successful
[server.c +82 main] recv_data.data=This buffer is in Client!!!!, recv_data.size=28
[server.c +83 main] recv_data.tcp_st=1, recv_data.run_cnt=1
[server.c +98 main] send ret=144
[server.c +100 main] [1] send to client successful:
[server.c +101 main] recv_data.data=This buffer is in Client!!!!, recv_data.size=28
[server.c +102 main] recv_data.tcp_st=1, recv_data.run_cnt=1
[server.c +108 main] run_cnt=1 then sleep(2)
[server.c +78 main] recv ret=144
[server.c +81 main] [2] recv from client successful
[server.c +82 main] recv_data.data=This buffer is in Client!!!!, recv_data.size=28
[server.c +83 main] recv_data.tcp_st=1, recv_data.run_cnt=2
[server.c +98 main] send ret=144
[server.c +100 main] [2] send to client successful:
[server.c +101 main] recv_data.data=This buffer is in Client!!!!, recv_data.size=28
[server.c +102 main] recv_data.tcp_st=1, recv_data.run_cnt=2
[server.c +108 main] run_cnt=2 then sleep(2)
[server.c +78 main] recv ret=144
[server.c +81 main] [3] recv from client successful
[server.c +82 main] recv_data.data=This buffer is in Client!!!!, recv_data.size=28
[server.c +83 main] recv_data.tcp_st=1, recv_data.run_cnt=3
[server.c +98 main] send ret=144
[server.c +100 main] [3] send to client successful:

客户端运行log

代码语言:txt
复制
[client.c +40 main] PID pid=3980
[client.c +46 main] tcp_server_port=9999
[client.c +47 main] tcp_server_ip=192.168.70.64
[client.c +59 main] then while send+recv
[client.c +71 main] send ret=144
[client.c +73 main] [1] sned to client successful:
[client.c +74 main] recv_data.data=, recv_data.size=0
[client.c +75 main] recv_data.tcp_st=0, recv_data.run_cnt=0
[client.c +84 main] recv ret=144
[client.c +87 main] [1] recv from client successful
[client.c +88 main] recv_data.data=This buffer is in Server!!!!, recv_data.size=28
[client.c +89 main] recv_data.tcp_st=1, recv_data.run_cnt=1
[client.c +98 main] run_cnt=1 then sleep(2)
[client.c +71 main] send ret=144
[client.c +73 main] [2] sned to client successful:
[client.c +74 main] recv_data.data=This buffer is in Server!!!!, recv_data.size=28
[client.c +75 main] recv_data.tcp_st=1, recv_data.run_cnt=1
[client.c +84 main] recv ret=144
[client.c +87 main] [2] recv from client successful

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档