网络通信——TCP/UDP编程

本文涉及的产品
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
实时计算 Flink 版,5000CU*H 3个月
简介: 关注TCP/UPD编程的过程

网络通信

网络通信概述

IP和端口

数据传输三要素:源、目的、长度 (网络传输中使用“IP和端口”表示源或目的)

image-20230611094115079

网络传输两个对象(server、client)

image-20230611094242702

两种传输方式:TCP/UDP

image-20230611094518466

image-20230611094625234

TCP和UDP原理上的区别

image-20230611094844914

TCP/UDP网络通信交互图(1为tcp,2为udp)

image-20230611095006598

image-20230611095022756

网络编程主要函数

socket函数

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

image-20230611095500594

bind函数

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

image-20230611095816172

listen函数

int listen(int sockfd, int backlog);

image-20230611095918099

accept函数

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

image-20230611100117742

connect函数

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

image-20230611100251241

send函数

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

image-20230611100421836

recv函数

 ssize_t recv(int sockfd, void *buf, size_t len, int flags);

image-20230611100511664

recvfrom函数

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

image-20230611100615424

sendto函数

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

image-20230611100715671

image-20230611100733684

TCP编程

server

#include <sys/types.h>    /* See NOTES */
#include <sys/socket.h> //网络编程所需文件
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>

/* socket
 * bind
 * listen
 * accept
 * send/recv
 */

#define SERVER_PORT 8888 // 服务器端口号
#define BACKLOG 10         // listen函数中最大同时监听连接数

int main(int argc, char **argv)
{
   
   
    int iSocketServer;
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr; // 服务器地址结构体
    struct sockaddr_in tSocketClientAddr; // 客户端地址结构体
    int iRet;
    int iAddrLen;

    int iRecvLen;
    unsigned char ucRecvBuf[1000];

    int iClientNum = -1;

    signal(SIGCHLD, SIG_IGN); // 忽略子进程退出信号,避免僵尸进程
    // 1.创建套接字 远程通信,TCP协议,0默认
    // 执行成功返回文件描述符,失败返回-1
    iSocketServer = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == iSocketServer)
    {
   
   
        printf("socket error!\n");
        return -1;
    }

    tSocketServerAddr.sin_family = AF_INET; // 网络通信
    // htons:主机字节序转网络字节序
    tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
    tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;     // 表示可以和任何主机通信
    memset(tSocketServerAddr.sin_zero, 0, 8);         // 填充0

    // 2.绑定本地地址和端口
    iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
    if (-1 == iRet)
    {
   
   
        printf("bind error!\n");
        return -1;
    }

    // 3.监听 10个连接
    iRet = listen(iSocketServer, BACKLOG);
    if (-1 == iRet)
    {
   
   
        printf("listen error!\n");
        return -1;
    }

    while (1)
    {
   
   
        iAddrLen = sizeof(struct sockaddr);
        /* 调用accept函数来等待客户端来连接,客户连接成功返回一个值,连接失败返回-1; */
        iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
        if (-1 != iSocketClient)
        {
   
   
            iClientNum++;
            /* 支持多个客户端连接,每有一个就调用fork(),并创建一个子进程 */
            // inet_ntoa:将网络字节序IP地址转换为点分十进制IP地址
            printf("Get connect from client %d : %s\n", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
            if (!fork()) /* 执行到fork()后马上复制一个代码完全一样的子进程*/
            {
   
                /* 父进程走fork()=0;子进程走fork()!=0; */
                         // fork函数:创建一个子进程,父进程返回子进程ID,子进程返回0 子进程的PID=父进程PID+1
                /*子进程的源码*/
                while (1)
                {
   
   
                    /* 接受客户端发来的数据并显示出来 */
                    iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0);
                    if (iRecvLen <= 0)
                    {
   
   
                        close(iSocketClient); /* 一直接受客户端传来的消息 */ // 关闭客户端套接字
                        return -1;
                    }
                    else
                    {
   
   
                        ucRecvBuf[iRecvLen] = '\0'; /* 接受到的数据以'\0'结尾 */
                        printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf);
                    }
                }
            }
        }
    }
    close(iSocketServer); // 关闭服务器套接字
    return 0;
}

client

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

/* socket
 * connect
 * send/recv
 */

#define SERVER_PORT 8888

int main(int argc, char **argv)
{
   
   
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;

    int iRet;
    unsigned char ucSendBuf[1000];
    int iSendLen;

    if (argc != 2)
    {
   
   
        printf("Usage:\n");
        printf("%s <server_ip>\n", argv[0]);
        return -1;
    }

    // 1.创建套接字
    iSocketClient = socket(AF_INET, SOCK_STREAM, 0); // 远程通信,TCP协议,0默认

    tSocketServerAddr.sin_family = AF_INET;
    tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
    // tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
    {
   
   
        printf("invalid server_ip\n");
        return -1;
    }
    memset(tSocketServerAddr.sin_zero, 0, 8);

    // 2.发送连接请求连接服务器
    iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
    if (-1 == iRet)
    {
   
   
        printf("connect error!\n");
        return -1;
    }

    while (1)
    {
   
   
        if (fgets(ucSendBuf, 999, stdin))
        {
   
   
            // 3.发送数据
            iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
            if (iSendLen <= 0)
            {
   
   
                // 4.关闭套接字
                close(iSocketClient);
                return -1;
            }
        }
    }

    return 0;
}

执行过程:在linux下先编译两个程序,然后执行server再打开新终端执行client,执行需要输入ip地址,此时在客户端发送字符串,服务器端可以接收到字符串

UDP编程

server

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

/* socket
 * bind
 * sendto/recvfrom
 */

#define SERVER_PORT 8888

int main(int argc, char **argv)
{
   
   
    int iSocketServer;
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;
    struct sockaddr_in tSocketClientAddr;
    int iRet;
    int iAddrLen;

    int iRecvLen;
    unsigned char ucRecvBuf[1000];

    int iClientNum = -1;

    // 1.创建socket套接字
    iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == iSocketServer)
    {
   
   
        printf("socket error!\n");
        return -1;
    }

    tSocketServerAddr.sin_family = AF_INET;
    tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
    tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    memset(tSocketServerAddr.sin_zero, 0, 8);

    // 2.绑定本地ip地址和端口号
    iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
    if (-1 == iRet)
    {
   
   
        printf("bind error!\n");
        return -1;
    }

    while (1)
    {
   
   
        iAddrLen = sizeof(struct sockaddr);
        // 3.接收数据 recvfrom常用于无连接的udp通信
        iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
        if (iRecvLen > 0)
        {
   
   
            ucRecvBuf[iRecvLen] = '\0';
            printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
        }
    }
    // 4.关闭socket套接字
    close(iSocketServer);
    return 0;
}

client

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

/* socket
 * connect
 * send/recv
 */

#define SERVER_PORT 8888

int main(int argc, char **argv)
{
   
   
    int iSocketClient;
    struct sockaddr_in tSocketServerAddr;

    int iRet;
    unsigned char ucSendBuf[1000];
    int iSendLen;

    if (argc != 2)
    {
   
   
        printf("Usage:\n");
        printf("%s <server_ip>\n", argv[0]);
        return -1;
    }
    // 1.创建socket套接字
    iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);

    tSocketServerAddr.sin_family = AF_INET;
    tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
    // tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
    if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
    {
   
   
        printf("invalid server_ip\n");
        return -1;
    }
    memset(tSocketServerAddr.sin_zero, 0, 8);

    // 2.连接服务器
    iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
    if (-1 == iRet)
    {
   
   
        printf("connect error!\n");
        return -1;
    }

    while (1)
    {
   
   
        if (fgets(ucSendBuf, 999, stdin))
        {
   
   
            // 3.发送数据
            iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
            if (iSendLen <= 0)
            {
   
   
                // 4.关闭套接字
                close(iSocketClient);
                return -1;
            }
        }
    }

    return 0;
}

总结:TCP和UPD编程的区别

TCP UDP
client socket->connect->send/recv socket->connect->send/recv(可以直接使用sendto,去掉connect)
server socket->bind->listen->accept->send/recv socket->bind->sendto/recvfrom
相关文章
|
2月前
|
负载均衡 网络协议 算法
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
这网络层就像搭积木一样,上层协议都是基于下层协议搭出来的。不管是ping(用了ICMP协议)还是tcp本质上都是基于网络层IP协议的数据包,而到了物理层,都是二进制01串,都走网卡发出去了。 如果网络环境没发生变化,目的地又一样,那按道理说他们走的网络路径应该是一样的,什么情况下会不同呢? 我们就从路由这个话题聊起吧。
78 4
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
|
2月前
|
网络协议
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。
|
2月前
|
监控 网络协议 网络性能优化
不再困惑!一文搞懂TCP与UDP的所有区别
本文介绍网络基础中TCP与UDP的区别及其应用场景。TCP是面向连接、可靠传输的协议,适用于HTTP、FTP等需要保证数据完整性的场景;UDP是无连接、不可靠但速度快的协议,适合DNS、RIP等对实时性要求高的应用。文章通过对比两者在连接方式、可靠性、速度、流量控制和数据包大小等方面的差异,帮助读者理解其各自特点与适用场景。
|
2月前
|
存储 网络协议 安全
用于 syslog 收集的协议:TCP、UDP、RELP
系统日志是从Linux/Unix设备及网络设备生成的日志,可通过syslog服务器集中管理。日志传输支持UDP、TCP和RELP协议。UDP无连接且不可靠,不推荐使用;TCP可靠,常用于rsyslog和syslog-ng;RELP提供可靠传输和反向确认。集中管理日志有助于故障排除和安全审计,EventLog Analyzer等工具可自动收集、解析和分析日志。
175 2
|
3月前
|
网络协议 网络性能优化 数据处理
深入解析:TCP与UDP的核心技术差异
在网络通信的世界里,TCP(传输控制协议)和UDP(用户数据报协议)是两种核心的传输层协议,它们在确保数据传输的可靠性、效率和实时性方面扮演着不同的角色。本文将深入探讨这两种协议的技术差异,并探讨它们在不同应用场景下的适用性。
117 4
|
3月前
|
监控 网络协议 网络性能优化
网络通信的核心选择:TCP与UDP协议深度解析
在网络通信领域,TCP(传输控制协议)和UDP(用户数据报协议)是两种基础且截然不同的传输层协议。它们各自的特点和适用场景对于网络工程师和开发者来说至关重要。本文将深入探讨TCP和UDP的核心区别,并分析它们在实际应用中的选择依据。
102 3
|
5月前
|
存储 网络协议 算法
UDP 协议和 TCP 协议
本文介绍了UDP和TCP协议的基本结构与特性。UDP协议具有简单的报文结构,包括报头和载荷,报头由源端口、目的端口、报文长度和校验和组成。UDP使用CRC校验和来检测传输错误。相比之下,TCP协议提供更可靠的传输服务,其结构复杂,包含序列号、确认序号和标志位等字段。TCP通过确认应答和超时重传来保证数据传输的可靠性,并采用三次握手建立连接,四次挥手断开连接,确保通信的稳定性和完整性。
143 1
UDP 协议和 TCP 协议
|
3月前
|
网络协议 算法 网络性能优化
|
3月前
|
网络协议 SEO
TCP连接管理与UDP协议IP协议与ethernet协议
TCP、UDP、IP和Ethernet协议是网络通信的基石,各自负责不同的功能和层次。TCP通过三次握手和四次挥手实现可靠的连接管理,适用于需要数据完整性的场景;UDP提供不可靠的传输服务,适用于低延迟要求的实时通信;IP协议负责数据包的寻址和路由,是网络层的重要协议;Ethernet协议定义了局域网的数据帧传输方式,广泛应用于局域网设备之间的通信。理解这些协议的工作原理和应用场景,有助于设计和维护高效可靠的网络系统。
74 4
|
4月前
|
网络协议 前端开发 物联网
TCP和UDP区别?
本文首发于微信公众号“前端徐徐”,详细介绍了TCP和UDP两种传输层协议的核心概念、连接性和握手过程、数据传输和可靠性、延迟和效率、应用场景及头部开销。TCP面向连接、可靠、有序,适用于网页浏览、文件传输等;UDP无连接、低延迟、高效,适用于实时音视频传输、在线游戏等。
103 1
TCP和UDP区别?