socket网络编程

下面分别以TCP、UDP为例介绍两者的流程图和代码实现:
  • TCP
1.流程图:
  和网上大部分的版本不同,我将TCP三次握手的过程画在了accpet之前,这也是在查阅一些资料发现三次握手会由内核自动完成,aceept只是从队列中取出已经建好的tcp连接,这一点可以在代码中不加入accpet以及后续步骤验证。

2.代码实现:
  server端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define SERVER_PORT 8888
#define SERVER_IP "192.168.1.100"

int main()
{
    // 1. 创建监听套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
        perror("fail fo socket!");
        return -1;
    }
    printf("create socket fd[%d] success!\n");

    // 2. 绑定监听套接字到本地IP和port
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &addr.sin_addr);
    int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror("fail to bind!");
        return -2;
    }
    printf("bind success!\n");

    // 3. 设置监听
    ret = listen(fd, 128);
    if (ret == -1)
    {
        perror("fail to listen!");
        return -3;
    }
    printf("listen success!\n");

    // 4. 阻塞等待,等待连接到达,连接成功后返回通信用的套接字
    struct sockaddr_in c_addr;
    int c_addr_len = sizeof(c_addr);
    int c_fd = accept(fd, (struct sockaddr*)&c_addr, &c_addr_len);
    if (c_fd == -1)
    {
        perror("fail to accept!");
        return -4;
    }
    printf("accept success!\n");

    // 5. 开始通信
    char buff[1024];
    char ip[32];
    memset(buff, 0, sizeof(buff));
    memset(ip, 0, sizeof(ip));
    int len = read(c_fd, buff, sizeof(buff));
    if (len > 0)
    {
        printf("client %s:%d >> %s\n", inet_ntop(AF_INET, &c_addr.sin_addr, ip , sizeof(ip)), ntohs(c_addr.sin_port), buff);
        write(c_fd, buff, len);
    }
    else if (len == 0)
    {
        printf("connect close!");
    }
    else
    {
        printf("connect fail!");
    }

    // 6. 关闭套接字
    close(fd);
    close(c_fd);
    return 0;
}
client端:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

#define SERVER_PORT 8888
#define SERVER_IP "192.168.1.100"

int main()
{
    // 1. 创建通信套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
        perror("fail fo socket!");
        return -1;
    }
    printf("create socket success!\n");

    // 2. 连接服务器
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &addr.sin_addr);
    int ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror("fail to connect!");
        return -2;
    }
    printf("connect success!\n");

    // 3. 通信
    char buff[1024];
    char ip[32];
    sprintf(buff, "Hello, this is a test connection with the server!");
    int len = send(fd, buff, strlen(buff) + 1, 0);
    memset(buff, 0, sizeof(buff));
    memset(ip, 0, sizeof(ip));
    len = recv(fd, buff, sizeof(buff), 0);
    if (len > 0)
    {
        printf("server %s:%d >> %s\n", inet_ntop(AF_INET, &addr.sin_addr, ip , sizeof(ip)), ntohs(addr.sin_port), buff);
    }
    else if (len == 0)
    {
        printf("connect close!\n");
    }
    else
    {
        perror("recv fail!\n");
    }

    // 4. 关闭套接字
    close(fd);
    return 0;
}
上述代码就是实现一个简单的socket通信过程,服务器和客户端收发一次消息后便关闭通信。
就是上面一个简单的通信过程,发现实现起来也并非一帆风顺,途中数次碰到问题,下面记录下踩到的一些坑:
1.fail to connect!: No route to host
server端代码和client端代码分别在两台虚拟机上运行时,client端一运行就报fail to connect!: No route to host错误,检查发现双发是能否ping通,搜索发现可能是防火墙打开导致这个问题,使用以下命令查看防火墙是开着的:
[root@localhost socket]# systemctl status firewalld.service
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2022-12-30 21:44:51 EST; 42min ago
     Docs: man:firewalld(1)
 Main PID: 693 (firewalld)
   CGroup: /system.slice/firewalld.service
           └─693 /usr/bin/python2 -Es /usr/sbin/firewalld --nofork --nopid

Dec 30 21:44:50 localhost.localdomain systemd[1]: Starting firewalld - dynamic firewall daemon...
Dec 30 21:44:51 localhost.localdomain systemd[1]: Started firewalld - dynamic firewall daemon.
Dec 30 21:44:51 localhost.localdomain firewalld[693]: WARNING: AllowZoneDrifting is enabled. This is considered an insecure configur...t now.
Hint: Some lines were ellipsized, use -l to show in full.
试着关闭防火墙后运行了下程序,发现能成功通信,果然是防火墙的原因。
2.server端显示client的ip和port错误
在运行上述代码时,发现在server端打印连接的client的ip和port一直都有问题并非实际的ip和port。排查了半天才发现传给accpet的第三个参数是表示sockaddr结构体的大小,并非随便传一个值都行。
struct sockaddr_in c_addr;
int c_addr_len = sizeof(c_addr);
int c_fd = accept(fd, (struct sockaddr*)&c_addr, &c_addr_len);

 

posted @ 2023-04-18 10:39  时间在哪  阅读(21)  评论(0)    收藏  举报