网络编程——TCP编程

news2024/12/26 23:03:44

TCP编程

  • 流程
    • 服务器
    • 客户端
  • 函数接口
    • 1、socket
    • 2、bind
    • 3、listen
    • 4、accept
    • 5、recv
    • 6、send
    • 7、connet
  • 实现双工通信
    • server.c
    • celient.c
    • 优化代码

流程

在这里插入图片描述
在C语言中进行TCP编程的一般步骤如下:

(1)包含头文件:
在代码中包含必要的头文件,以便使用TCP编程所需的函数和数据类型。通常情况下,你需要包含 <sys/socket.h>、<netinet/in.h> 和 <arpa/inet.h>。

(2)创建套接字:
使用 socket() 函数创建一个套接字,该套接字将用于网络通信。套接字是一个整数值,它表示一个打开的文件描述符,用于在网络上发送和接收数据。

(3)设置地址和端口:
创建一个 struct sockaddr_in 结构体,并设置其中的成员变量,包括地址和端口号。这个结构体用于指定服务器的地址和端口。

(4)绑定套接字
使用 bind() 函数将套接字绑定到指定的地址和端口上。这将使服务器能够监听指定的端口并接受客户端的连接。

(5)监听连接:
使用 listen() 函数开始监听连接请求。这将使服务器进入被动等待状态,等待客户端连接。

(6)接受连接:
使用 accept() 函数接受客户端的连接请求。当有客户端连接到服务器时,accept() 函数将返回一个新的套接字,该套接字用于与客户端进行通信。

(7)通信:
使用 send() 和 recv() 函数发送和接收数据。服务器和客户端都可以使用这些函数来发送和接收数据。

(8)关闭连接:
在通信结束后,使用 close() 函数关闭套接字,释放资源。

这些步骤提供了一个基本的框架来进行TCP编程。你可以根据需要进行适当的修改和扩展。同时还需要处理错误和异常情况,并确保适当地释放资源,以避免内存泄漏和其他问题。

在这里插入图片描述

服务器

(1)socket:创建一个用与链接的套接字(用于链接)

(2)bind:绑定自己的ip地址和端口

(3)listen:监听,将主动套接字转为被动套接字

(4)accept:阻塞等待客户端链接,链接成功返回一个用于通信套接字

(5)recv:接收消息

(6)send:发送消息

(7)close:关闭文件描述符

客户端

(1)socket:创建一个套接字

(2)填充结构体:填充服务器的ip和端口

(3)connect:阻塞等待链接服务器

(4)recv/send:接收/发送消息

(5)close:关闭

函数接口

1、socket

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
 功能:创建套接字文件  
 参数:
     domain:协议族 ,选择通信方式
        AF_UNIX, AF_LOCAL   本地通信
	AF_INET             IPv4 
	AF_INET6            IPv6  
    type:通信协议-套接字类型
	SOCK_STREAM   流式套接字
	SOCK_DGRAM    数据报套接字
	SOCK_RAW      原始套接字
    protocol:协议  填0,自动匹配底层TCP或UDP等协议。根据type匹配系统默认自动帮助匹配对应协议
     传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
     网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)

返回值:成功。返回同于链接的文件描述符
       失败 -1,更新errno

2、bind

```c
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
 功能:绑定套接字 - ip和端口
 功能:
   sockfd:套接字文件描述符
   addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应结构体-通信结构体由socket第一个参数确定)

  addrlen:结构体大小
返回值: 成功0
      失败:-1 更新errno

 通用结构体:  
struct sockaddr{
	sa_family_t sa_family;
	char        sa_data[14];
}

 ipv4的通信结构体:
struct sockaddr_in{
	sa_family_t    sin_family;/*AF_INET */
	in_port_t      sin_port;/* 端口 */
	struct in_addr sin_addr;/* ip地址 */
};
struct in_addr{
	uint32_t       s_addr;
};

本地通信结构体:
struct sockaddr_un{
	sa_family_t sun_family;/* AF_UNIX */
	char        sun_path[108];/* 套接字文件 */
};


3、listen

int listen(int sockfd,int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
 sockfd:套接字
 backlog:同时响应客户端请求链接的最大个数,不能写0.
  不同平台可同时链接的数不同,一般写6-8(队列1:保存正在连接)
(队列2,连接上的客户端)
   返回值:成功 0   失败-1,更新errno 

4、accept

int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen);
accept(sockfd,NULL,NULL);
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,
则accept()函数返回,返回一个用于通信的套接字文件;
参数:
   Sockfd :套接字
   addr: 链接客户端的ip和端口号
      如果不需要关心具体是哪一个客户端,那么可以填NULL;
   addrlen:结构体的大小
     如果不需要关心具体是哪一个客户端,那么可以填NULL;
  返回值: 
     成功:文件描述符;//用于通信
失败:-1,更新errno
 //监听套接字,将主动套接字转为被动套接字
    if (listen(sockfd, 5) < 0)
    {
        perror("listern error.");
        return -1;
    }
    //4.阻塞等待客户端链接,链接成功返回一个用于通信的文件描述符
    acceptfd = accept(sockfd, NULL, NULL);
    if (acceptfd < 0)
    {
        perror("accept error.");
        return -1;
    }
    printf("acceptfd=%d\n", acceptfd);

5、recv

ssize_t recv(int sockfd,void*buf, size_t len,int flags);
功能: 接收数据 
参数: 
    sockfd: acceptfd ;
    buf  存放位置
    len  大小
    flags  一般填0,相当于read()函数
    MSG_DONTWAIT  非阻塞
返回值: 
	<0  失败出错  更新errno
	==0  表示客户端退出
	>0   成功接收的字节个数
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //创建套接字
    int sockfd;
    int acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error.");
        return -1;
    }
    printf("sockfd=%d\n", sockfd);
    //填充ipv4的通信结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8888);
    serveraddr.sin_addr.s_addr = inet_addr("192.168.50.83");

    //绑定套接字
    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind error.");
        return -1;
    }
    printf("bind ok.\n");
    //3.监听套接字,将主动套接字转为被动套接字
    if (listen(sockfd, 5) < 0)
    {
        perror("listern error.");
        return -1;
    }
    //4.阻塞等待客户端链接,链接成功返回一个用于通信的文件描述符
    acceptfd = accept(sockfd, NULL, NULL);
    if (acceptfd < 0)
    {
        perror("accept error.");
        return -1;
    }
    printf("acceptfd=%d\n", acceptfd);
    //5.循环接收消息
    char buf[128];
    int recvbyte;
    while (1)
    {
        recvbyte = recv(acceptfd, buf, sizeof(buf), 0);
        if (recvbyte < 0)
        {
            perror("recv error.");
            return -1;
        }
        else if (recvbyte == 0)
        {
            printf("client exit.\n");
            break;
        }
        else
        {
            buf[recvbyte] = '\0';
            printf("buf:%s\n", buf);
        }
    }
    close(acceptfd);
    close(sockfd);
    return 0;
}

6、send

ssize_t send(int sockfd,constvoid*buf, size_t len,int flags);
功能:发送数据
参数:
    sockfd:socket函数的返回值
    buf:发送内容存放的地址
    len:发送内存的长度
    flags:如果填0,相当于write();
返回值: 
<0  失败出错  更新errno
>0   成功发送的字节个数

7、connet

int connect(int sockfd,const struct sockaddr*addr,socklen_t addrlen);
功能:用于连接服务器;
参数:
     sockfd:socket函数的返回值
     addr:填充的结构体是服务器端的;
     addrlen:结构体的大小
返回值 
-1 失败,更新errno
      正确 0
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //创建套接字
    int sockfd;
    int acceptfd;
    char buf[128];
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error.");
        return -1;
    }
    printf("sockfd=%d\n", sockfd);
    //填充ipv4的通信结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(8888);
    serveraddr.sin_addr.s_addr = inet_addr("192.168.50.83");
   
    //连接到服务器端
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("connect err.");
        return -1;
    }
    printf("connect success.\n");
    ssize_t printbyte;
    while (1)
    {
        memset(buf, 0, sizeof(buf));                         
        printf("send:");
        scanf("%s", buf);
      
        if ((printbyte = send(sockfd, buf, sizeof(buf), 0)) <0)
        {
            perror("send err.");
        }
    }

    return 0;
}

实现双工通信

server.c

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

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("please input %s <port>\n", argv[0]);
        return -1;
    }
    int sockfd, acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    struct sockaddr_in serveraddr, caddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[1]));
    // serveraddr.sin_addr.s_addr=inet_addr(argv[1]);

    //自动获取ip
    //serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
    // serveraddr.sin_addr.s_addr=INADDR_ANY; //0.0.0.0
    serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0");

    socklen_t len = sizeof(caddr);

    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }

    if (listen(sockfd, 5) < 0)
    {
        perror("listen err.");
        return -1;
    }
    while (1)
    {
        acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
        if (acceptfd < 0)
        {
            perror("accept err.");
            return -1;
        }
        printf("client:ip=%s  port=%d\n",
               inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

        char buf[128];
        pid_t pid = fork();
        if (pid < 0)
        {
            perror("fork err.");
            return -1;
        }
        else if (pid == 0) //发送
        {
            int sendbyte;
            while (1)
            {
                fgets(buf, sizeof(buf), stdin);
                if (buf[strlen(buf) - 1] == '\n')
                    buf[strlen(buf) - 1] = '\0';
                sendbyte = send(acceptfd, buf, sizeof(buf), 0);

                if (sendbyte < 0)
                {
                    perror("send error.");
                    return -1;
                }
            }
        }
        else
        {

            int recvbyte;
            while (1)
            {
                recvbyte = recv(acceptfd, buf, sizeof(buf), 0);
                if (recvbyte < 0)
                {
                    perror("recv err.");
                    return -1;
                }
                else if (recvbyte == 0)
                {
                    printf("client exit.\n");
                    kill(pid,SIGKILL);
                    wait(NULL);
                    break;
                }
                else
                {
                    printf("buf:%s\n", buf);
                }
            }
            close(acceptfd);
        }
    }
    close(sockfd);
    return 0;
}

celient.c

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


int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        printf("please input %s <ip> <port>\n", argv[0]);
        return -1;
    }
    //1.创建套接子
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket error.");
        return -1;
    }
    printf("sockfd=%d\n", sockfd);
    //填充ipv4的通信结构体  服务器的ip和端口
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    //2.请求链接服务器
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("connect error.");
        return -1;
    }

    //5.循环发送消息  通信
    char buf[128];
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork err.");
        return -1;
    }
    else if (pid == 0)
    {
        int recvbyte;
        while (1)
        {
            recvbyte = recv(sockfd, buf, sizeof(buf), 0);
            if (recvbyte < 0)
            {
                perror("recv err.");
                return -1;
            }
            printf("buf:%s\n", buf);
        }
    }
    else
    {
        int sendbyte;
        while (1)
        {
            fgets(buf, sizeof(buf), stdin);
            if (buf[strlen(buf) - 1] == '\n')
                buf[strlen(buf) - 1] = '\0';
            sendbyte = send(sockfd, buf, sizeof(buf), 0);
            if (sendbyte < 0)
            {
                perror("send error.");
                return -1;
            }
            if(strncmp(buf,"quit",4)==0)
              {
                  kill(pid,SIGKILL);
                   wait(NULL);
                  exit(-1);
              }
        }
    }
    close(sockfd);
    return 0;
}

优化代码

1.去掉fget获取多余的‘\n’
if(buf[strlen(buf)-1]=='\n')
         buf[strlen(buf)-1]='\0';
2.端口和ip地址通过命令行传参到代码中。
3.设置客户端退出,服务器结束循环接收。
    通过recv返回值为0判断客户端是否退出。
4.设置来电显示功能,获取到请求链接服务器的客户端的ip和端口。
5.设置服务器端自动获取自己的ip地址。
   INADDR_ANY  "0.0.0.0"
6.实现循环服务器,服务器不退出,当链接服务器的客户端退出,服务器等到下一个客户端链接。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/530654.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面对象QgsPolygon

几何对象中的面用QgsPolygon进行封装&#xff0c;也称为多边形简单的多边形是由一串点连接而成&#xff0c;并首尾闭合多边形的结构更复杂&#xff0c;除了有一个外部轮廓&#xff0c;还可能包括内部多个轮廓 创建面对象 QgsPolygon() #创建一个空的面 使用setExteriorRing设…

Spring AOP 实践指南

Spring AOP 实践指南 文章目录 Spring AOP 实践指南一、概述1、简介2、官方资料3、本文档说明 二、基本使用1、引入依赖2、定义切面3、定义切点4、创建 HelloController5、启动项目&#xff0c;访问测试 三、通知1、概述五种通知通知的顺序 2、通知方法接受的参数3、前置通知代…

Thread线程学习(1) 了解线程的基本知识——什么是线程

本专栏将记录有关线程方面的知识 在计算机科学领域中&#xff0c;线程&#xff08;Thread&#xff09;是一种执行计算机程序的基本单元。对于初学者来说&#xff0c;理解线程是学习并发编程的关键一步。本文将带你了解线程的基础知识&#xff0c;包括线程的定义、线程与进程的关…

GPT神奇应用:给孩子做每日安排

正文共 1163 字&#xff0c;阅读大约需要 4 分钟 家长必备技巧&#xff0c;您将在4分钟后获得以下超能力&#xff1a; 快速生成每日安排计划 Beezy评级 &#xff1a;B级 *经过简单的寻找&#xff0c; 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●图片…

JavaScript实现打印倒金字塔的代码

以下为实现打印倒金字塔的程序代码和运行截图 目录 前言 一、实现打印倒金字塔 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可以根据题目要求实现相关使用功…

动态规划专题

动态规划专题 最长递增子序列LeetCode 300. 最长递增子序列解题思路代码实现 LeetCode 354. 俄罗斯套娃信封问题解题思路代码实现 总结 不要纠结&#xff0c;干就完事了&#xff0c;熟练度很重要&#xff01;&#xff01;&#xff01;多练习&#xff0c;多总结&#xff01;&…

【Redis】Redis中bitmap的原理和使用

文章目录 一、原理二、BitMap 相关命令三、BitMap 空间计算四、使用场景1. 用户签到2. 统计活跃用户&#xff08;用户登陆情况&#xff09;3. 统计用户在线状态4. 实现布隆过滤器 五、总结 一、原理 先声明一下&#xff1a;Redis 有5种数据类型&#xff0c;而 BitMap 在 Redis…

【k8s】Ubuntu22.04离线部署k8s集群:搭建软件仓库和镜像仓库(repo节点)

上两篇主要记录了在CentOS 7环境中离线部署k8s的方案&#xff0c;本篇继续介绍方案二在Ubuntu 22.04的实现。&#xff08;当然&#xff0c;整体思路还是跟上篇基本相似&#xff09; 目录 Ubuntu22.04离线部署k8s集群&#xff1a;搭建软件仓库和镜像仓库&#xff08;repo节点&am…

总结852

学习目标&#xff1a; 月目标&#xff1a;5月&#xff08;张宇强化前10讲&#xff0c;背诵15篇短文&#xff0c;熟词僻义300词基础词&#xff09; 周目标&#xff1a;张宇强化前5讲并完成相应的习题并记录&#xff0c;英语背3篇文章并回诵 每日必复习&#xff08;5分钟&#…

云上高校导航 小程序 开发教程 1.0.1

​ Gitee仓库&#xff1a;云上高校导航 GitHub仓库&#xff1a;云上高校导航 “云上高校导航”是一套基于小程序云开发的校园导航类系统开发方案&#xff0c;该开发方案可供开发者进行二次开发&#xff0c;用于解决师生和访客的校园出行需求。 项目优势及创新&#xff1a;…

ChatGPT vs. Bing vs. Bard

随着 2022 年 ChatGTP 的推出&#xff0c;人工智能聊天机器人的世界突然走上了一条新道路。如今&#xff0c;密切关注 AI 的人都知道&#xff0c;不同公司推出了几款产品。从谷歌拥有自己的 Bard AI&#xff0c;到微软发布新的 Bing AI Chat&#xff0c;再到 OpenAI 发布GPT-4。…

云服务器搭建Python项目实现学术优化chatgpt

云服务器搭建实现学术优化chatgpt 1 服务器准备2 云服务器配置2.1 python虚拟环境2.1.1 python3.9安装配置2.1.2 下载python项目2.1.3 创建python虚拟环境 3 后台运行python项目&#xff08;不然不能关闭与云服务器的连接&#xff0c;那意义何在&#xff1f;&#xff09; 1 服务…

GEE:将年度NDVI时间序列影像集合(Image Collection)转变为多波段影像,并下载

作者:CSDN @ _养乐多_ 本文将重点介绍如何使用 Google Earth Engine (GEE) 将多波段影像堆叠并导出,并探讨其应用场景和好处。 通过使用 GEE 的多波段影像堆叠功能,我们可以将不同波段的遥感影像整合成一个多波段影像,以支持各种地理空间分析任务。这种方法适用于遥感影…

以太网端口类型

以太网端口有 3种链路类型:access、trunk、hybird Access类型端口只能属于1个VLAN 般用于连接计算机 端口&#xff1b;Trunk类型端口可以允许多个VLAN通过,可以接收和发送多个VLAN 报文,一般用于交换机之间的连接&#xff1b;Hybrid类型端口可以允许多个VLAN通过&#xff0c;可…

Dubbo的使用

Dubbo在开发中&#xff0c;存在两种开发思路。基于SOA(面向服务的体系架构)思想和辅助SpringCloud架构提升效率。 Dubbo 默认使用 Netty 框架 Dubbo基于SOA思想 正常SpringBoot项目是只有一个启动类&#xff0c;接口定义在web层(即Controller层)中,然后调用Service层。&…

Matlab 梯度下降法

一、简介 梯度下降法&#xff08;Gradient descent&#xff09;或最速下降法&#xff08;steepest descent&#xff09;是求解无约束最优化问题的一种常用方法。 假设fx)在R上具有一阶连续偏导数的函数。要求解的无约束最优化问题是。其本质是一个迭代的方法&#xff0c;选择…

VMware Workstation 网络备忘 + 集群规模

概述 在虚拟机中部署服务&#xff0c;进行IP规划&#xff0c;进行相关的前期准备 3 张网卡 2个不同的网段 1个NAT 概述截图 NAT 截图 VMnet0 截图 VMnet1 截图 总结&#xff1a; 网卡&#xff08;网络适配器&#xff09;名称IP网段备注NATens33192.168.139.0VMnet0ens34VMne…

手把手教你在RT-THREAD bsp上运行pikascript脚本点亮小灯

简介 这篇文章介绍如何在RT-THREAD bsp上运行pikascript脚本。 pikascript相当于一个小型的micropython。 最近有一些结构上的调整&#xff0c;写这篇文章大概介绍一下如何使用&#xff0c;以及开发过程中需要注意的问题。 这篇文章几乎适配所有的RT-THREAD上的bsp。&#xff…

在Kubernetes(K8S) 上运行第一个应用

1、启动代理 &#xff1a; kubectl proxy 2、部署应用程序最简单的方式是使用 kubectl run 命令&#xff0c;该命令可以创建所有必要的组件而无需JSON或YAML文件。 --imageluksa/kubia 显示的是指定要运行的容器镜像&#xff0c;--port8080 选项告诉Kubernetes应用正在监听808…

每日一算-冒泡排序

冒泡排序是最简单的排序算法&#xff0c;如果相邻元素的顺序错误&#xff0c;则通过重复交换它们来工作。该算法不适用于大数据集&#xff0c;因为它的平均和最坏情况时间复杂度都很高。 原理 输入&#xff1a; arr[] {6, 3, 0, 5} 第一步&#xff1a; 冒泡排序从最前面的两个…