socket编程(2) -- TCP通信

news2024/9/21 5:40:33

TCP通信

  • 2. 使用 Socket 进行TCP通信
    • 2.1 socket相关函数介绍
    • socket()
    • bind()
    • listen()
    • accept()
    • connect()
    • 2.2 TCP协议 C/S 模型
    • 基础通信代码
  • 最后

2. 使用 Socket 进行TCP通信

Socket通信流程图如下:
在这里插入图片描述

  这里服务器段listen是监听socket套接字的监听文件描述符。如果客户端有连接请求,服务器端会自动和客户端建立连接,这里的accept函数,只是从已经建立了连接的已连接队列中取出一个建立的客户端连接,并返回用于数据传输的文件描述符。

2.1 socket相关函数介绍

socket()

//socket函数
#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
返回值:
	成功返回socket的文件描述符,失败返回-1,并且通过设置错误信息errno
参数:
    domain:可以选取下面的参数,常用的是AF_INET,AF_INET6,AF_UNIX
    	Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   Local communication              unix(7)
       AF_INET             IPv4 Internet protocols          ip(7)
       AF_INET6            IPv6 Internet protocols          ipv6(7)
       AF_IPX              IPX - Novell protocols
       AF_NETLINK          Kernel user interface device     netlink(7)
    type:可以选取下面的参数,常用的是用于tcp通信的SOCK_STREAM,和udp通信的数据包SOCK_DGRAM
    	SOCK_STREAM     Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.
       	SOCK_DGRAM      Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
		SOCK_SEQPACKET  Provides  a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire packet with each input system call.
		SOCK_RAW        Provides raw network protocol access.
		SOCK_RDM        Provides a reliable datagram layer that does not guarantee ordering.
		SOCK_PACKET     Obsolete and should not be used in new programs; see packet(7).
        上面的参数还可以或上(|)下面的两个参数来添加额外属性:
        SOCK_NONBLOCK   Set the  O_NONBLOCK file status flag on the new open file description.  Using this flag saves extra calls to fcntl(2) to achieve the same result.
		 SOCK_CLOEXEC   Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor.  See the  description  of  the  O_CLOEXEC flag in open(2) for reasons why this may be useful.
	protocol: 指定这个socket类型使用的协议,如果这个socket类型只有一个协议,那么这个参数设置为0

bind()

#include <sys/types.h> 
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:
    成功返回0,失败返回-1,并设置errno值
参数:
	sockfd:	使用socket函数成功返回的文件描述符
    addr:socket地址结构体,这里使用sockaddr_in结构体代替,可以接受的客户端ip和端口
        struct sockaddr {
            sa_family_t sa_family;
            char        sa_data[14];
        }

		struct sockaddr_in {
            sa_family_t    sin_family; /* address family: AF_INET */
            in_port_t      sin_port;   /* port in network byte order */
            struct in_addr sin_addr;   /* internet address */
        };
		/* Internet address. */
		struct in_addr {
    		uint32_t       s_addr;     /* address in network byte order */
		};
	addrlen:sockaddr_in结构体大小,sizeof(addr)

listen()

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int listen(int sockfd, int backlog);
返回值:
    成功返回0,失败返回-1,并设置errno
参数:
    sockfd:socket文件描述符,同上
    backlog:排队建立3次握手队列和刚刚建立3次握手队列的链接数和,例如可以设置为1024

accept()

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:
    成功,系统掉用会返回一个非负的整数,这个整数就是已经连接的socket文件描述符,失败返回-1,并设置errno值。
参数:
    sockfd:同上
    addr:传出参数,取出的这个连接的socket文件描述符的客户端地址参数,设置为NULL表示不需要传出
    addrlen:传出地址结构体的大小, sizeof(addr),前面为NULL,则它设为NULL

connect()

#include <sys/types.h> 
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:
	成功返回0,失败返回-1,并设置errno
参数:
	sockfd: 客户端socket文件描述符
    addr: 传入参数,指定服务器的地址和端口
    addrlen: 上面结构体的大小 sizeof(addr)

2.2 TCP协议 C/S 模型

在这里插入图片描述

为了方便错误处理,可以对上面函数进行封装后使用

//wrap.h
#ifndef __WRAP_H_
#define __WRAP_H_
void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
int Bind(int fd, const struct sockaddr *sa, socklen_t salen);
int Connect(int fd, const struct sockaddr *sa, socklen_t salen);
int Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
int Close(int fd);
#endif


//wrap.c
#include <wrap.h>

void perr_exit(const char *s)
{
    perror(s);
    exit(1);
}
// 确定这是一个什么类型的socket,可以接收哪种协议
int Socket(int family, int type, int protocol)
{
    int sfd;
    if ((sfd = socket(family, type, protocol)) < 0)
        perr_exit("socket error");
    return sfd;
}

// 绑定sfd的ip和端口,成功返回0,失败返回-1
int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    int n;
    // 成功返回0
    if ((n = bind(fd, sa, salen)) < 0)
        perr_exit("bind error");
    return n;
}

// 监听sfd并自动与连接请求建立连接,监听成功返回0,失败返回-1
int Listen(int fd, int backlog)
{
    int n;
    if ((n = listen(fd, backlog)) < 0)
        perr_exit("listen error");
    return n;
}

/* 取出一个已经和服务器sfd的socket建立连接的连接队列中取出一个客户端sfd,
后两个都是传出参数,是客户端socket的信息
返回客户端文件描述符*/
int Accept(int sfd, struct sockaddr *sa, socklen_t *clientsocketlenptr)
{
    int n;
reaccept:
    if ((n = accept(sfd, sa, clientsocketlenptr)) < 0)
    {
        // 防止该阻塞函数被无关的信号打断
        if ((errno == ECONNABORTED) || (errno == EINTR))
            goto reaccept;
        else
            perr_exit("accept error");
    }
    return n;
}

/*客户端发起连接,sfd为客户端socket文件描述符,
后两个参数是服务器端的ip和端口
连接成功返回0,失败返回-1*/
int Connect(int sfd, const struct sockaddr *sa, socklen_t salen)
{
    int n;
    if ((n = connect(sfd, sa, salen)) < 0)
        perr_exit("connect error");
    return n;
}

/*从cfd文件描述符中读取数据到 buf 中
成功,返回读取到的字符串长度,如果返回0表示读到末尾,失败返回-1
*/
ssize_t Read(int cfd, void *buf, size_t buflen)
{
    ssize_t n;
readagain:
    if ((n = read(cfd, buf, buflen)) == -1)
    {
        if (errno == EINTR)
            goto readagain;
        else
            return -1;
    }
    else if (n == 0)
    {
        printf("read end of file\n");
    }

    return n;
}

ssize_t Write(int cfd, const void *buf, size_t buflen)
{
    ssize_t n;
writeagain:
    if ((n = write(cfd, buf, buflen)) == -1)
    {
        if (errno == EINTR)
            goto writeagain;
        else
            return -1;
    }
    else if (n == 0)
    {
        printf("write end of file\n");
    }
    return n;
}

int Close(int fd)
{
    int n;
    if ((n = close(fd)) == -1)
        perr_exit("close error");
    return n;
}

基础通信代码

服务器单进程处理客户端连接和数据通信,主要通过while循环来实现。

//server.c
#include <wrap.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>

void showClient(const struct sockaddr_in *clientaddr)
{
    char buf[16];
    memset(buf, 0x00, sizeof(buf));
    inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, buf, sizeof(buf));

    printf("client family is[%d], ip is[%s] ,port is [%d]----connected\n", clientaddr->sin_family, buf, ntohs(clientaddr->sin_port));
}

int main()
{
    int sfd = Socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr;
    bzero(&addr, 0x00);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    Bind(sfd, (struct sockaddr *)&addr, sizeof(addr));

    Listen(sfd, 1024);

    struct sockaddr_in clientaddr;
    bzero(&clientaddr, 0x00);

    int cfd;
    char buf[64];
    int n;
    socklen_t len;
    while (1)
    {
        n = 0;
        len = sizeof(clientaddr);
        cfd = Accept(sfd, (struct sockaddr *)&clientaddr, &len);
        showClient(&clientaddr);
        while (1)
        {
            memset(buf,0x00,sizeof(buf));
            n = Read(cfd, buf, sizeof(buf));
            if (n==0)
            {
                break;
            }
            printf("[%d] byte word,client send say:[%s]\n", n, buf);
            int i = 0;
            for (i = 0; i < n; i++)
            {
                buf[i] = toupper(buf[i]);
            }
            Write(cfd, buf, n);

        }
    }

    close(cfd);
    close(sfd);

    return 0;
}
//client.c
#include <wrap.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <ctype.h>

void showClient(const struct sockaddr_in *clientaddr)
{
    char buf[16];
    memset(buf, 0x00, sizeof(buf));
    inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, buf, sizeof(buf));
    printf("client family is[%d], ip is[%s] ,port is [%d]----connected\n", clientaddr->sin_family, buf, clientaddr->sin_port);
}

int main()
{
    int cfd = Socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr;
    bzero(&addr, 0x00);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr);

    Connect(cfd, (struct sockaddr *)&addr, sizeof(addr));

    char words[64];
    int n;

    while (1)
    {
        memset(words, 0x00, sizeof(words));
        // scanf("%s", words);
        //读标准输入数据
		n = read(STDIN_FILENO, words, sizeof(words));

        Write(cfd, words, strlen(words));

        n = Read(cfd, words, sizeof(words));
        printf("server reply [%s],byte is [%d]\n", words, n);
    }

    return 0;
}

最后

推荐一个零声教育学习教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:链接

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

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

相关文章

稳!连续五年蝉联第一,华为UPS何以领跑市场?

数字经济的蓬勃发展、人工智能浪潮的兴起&#xff0c;使得数据中心产业正经历一场影响深远的变革。 这其中&#xff0c;作为数据中心电能质量治理和不间断供电的核心组件&#xff0c;UPS&#xff08;不间断电源&#xff0c;Uninterruptible Power Supply&#xff09;堪称数据中…

python数据可视化(1)——绘制柱状图

课程学习来源&#xff1a;b站up&#xff1a;【蚂蚁学python】 【课程链接&#xff1a;【【数据可视化】Python数据图表可视化入门到实战】】 【课程资料链接&#xff1a;【链接】】 #导入数据 import pandas as pd df pd.read_excel("../DATA_POOL/PY_DATA/ant-learn-vi…

楼宇智慧公厕如何做到厕位状态、环境数据实时监控

在现代化的楼宇中&#xff0c;智慧公厕的出现为人们提供了更加便捷、舒适和卫生的如厕环境。其中&#xff0c;厕位状态和环境数据的实时监控是其关键特性之一。 一、楼宇智慧公厕简介 楼宇智慧公厕是一种融合了先进技术的现代化公厕设施&#xff0c;旨在提升用户体验、提高管理…

STL(一)

书写形式&#xff1a;string (const string& str, size_t pos, size_t len npos); 举例&#xff1a; int main(){ string url("https://mp.csdn.net/mp_blog/creation/editor?spm1000.2115.3001.4503") string sub1(url,0,5);//从下标为0开始向后5个字符&…

树莓派pico入坑笔记,dht11使用及温湿度表制作

目录 关于树莓派pico和circuitpython的更多玩法&#xff0c;请看树莓派pico专栏 用到的库adafruit_dht&#xff0c;需要导入pico才能使用&#xff0c;在这里下载 样例程序 进阶玩法&#xff0c;显示信息的温湿度计 屏幕使用见树莓派pico专栏的ssd1306oled屏幕使用 代码 效…

Excel的Index+MATCH组合使用方法

INDEX函数 INDEX函数作用&#xff1a;用于从指定的单元格区域中返回特定行和列的值。 参数形式为&#xff1a;INDEX(array, row_num, [column_num]) array&#xff1a;必需。单元格区域或数组常量。 row_num&#xff1a;必需。要返回的值所在的行号。 [column_num]&#x…

开发者必读:获取电商API的多种渠道

开发电商软件往往需要对接电商API&#xff0c;电商API可以从哪些渠道获取&#xff1f;下面给大家介绍两种获取渠道。 一、从电商平台开放平台获取电商API 电商平台的开放平台是获取电商API最直接的渠道&#xff0c;但是电商平台较多&#xff0c;每一个电商平台都需要单…

【理解串】

目录 一、串的基本概念二、串的基本操作及实现三、串的存储实现3.1、静态数组实现3.2、动态数组实现 四、串的朴素模式匹配4.1、算法思想4.2、代码实现 五、KMP算法5.1、算法思想5.2、求模式串的next数组5.2、代码实现 一、串的基本概念 串&#xff1a;即字符串&#xff08;st…

常见的点云数据的获取方式

1. 激光雷达&#xff08;LiDAR&#xff09; 获取方式&#xff1a;激光脉冲测距原理&#xff1a;激光雷达通过发射激光脉冲并接收反射信号来测量物体与传感器之间的距离。计算激光脉冲从发射到返回所需的时间&#xff0c;并将其转换为距离&#xff0c;从而生成三维点云数据。常…

上班摸鱼吗?一文详解代码生成神器-Velocity

引言 “我不是在教你学坏,而是教你如何提高生产效率。” ----------- 牛顿 人类社会能够一直进步发展出现在的文明世界,最大的一个原因就是这个世界上懒人居多,懒人为了偷懒就需要提高生产效率,效率提高节省下来的时间才能创造出艺术、娱乐以及更高效率的科学技术。程序员…

丑数问题,力扣264,坑点

丑数问题&#xff0c;力扣264&#xff0c;坑点 力扣链接 给你一个整数 n &#xff0c;请你找出并返回第 n 个 丑数 。 丑数 就是质因子只包含 2、3 和 5 的正整数。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;12 解释&#xff1a;[1, 2, 3, 4, 5, 6, 8, 9, …

33 IRF配置思路

IRF配置思路网络括谱图 主 Ten-GigabitEthernet 1/0/49 Ten-GigabitEthernet 1/0/50 Ten-GigabitEthernet 1/0/51 备 Ten-GigabitEthernet 2/0/49 Ten-GigabitEthernet 2/0/50 Ten-GigabitEthernet 2/0/51 思路 主 1 利用console线进入设备的命令行页面去更改…

Spark源码详解

https://www.cnblogs.com/huanghanyu/p/12989067.html#_label3_3

服了,jenkins找不到advanced

新手下载的最新版本&#xff0c;过新手入门的时候一直过不去&#xff0c;就跳过了。 想下载一个汉化&#xff0c;还下载不了。根据提示搜索&#xff0c;结果大家让去advanced找url&#xff0c;也找不到。

[C++] 模拟实现list(二)

标题&#xff1a;[C] 模拟实现list&#xff08;二&#xff09; 水墨不写bug 目录 &#xff08;一&#xff09;回顾 &#xff08;二&#xff09;迭代器类的封装设计 &#xff08;1&#xff09;成员函数简要分析 &#xff08;2&#xff09;const迭代器类的设计 &#xff08;…

国漫推荐08

仙侠、武侠、恋爱、战斗、现代、古风 1.《仙王的日常生活》仙侠、日常、搞笑 《仙王的日常生活》第一季 《仙王的日常生活 第二季》 《仙王的日常生活 第三季》 《仙王的日常生活 第四季》 2.《风灵玉秀》武侠、少女 3.刺客伍六七 番名季度上映时间《伍六七》第一季2018-04-…

ppt如何翻译最高效?盘点5个便捷易用的ppt翻译器

正值夏日炎炎&#xff0c;7.15初伏即将到来&#xff0c;天气更是开始热到让人无法专心工作~面对电脑上一堆非母语的PPT文件&#xff0c;看得人愈发烦躁。 幸运的是&#xff0c;我手里头常常备着几款ppt翻译工具&#xff0c;这才让办公显得没那么枯燥和烦闷~倘若你也遇上同样的…

技术速递|宣布为 .NET 升级助手提供第三方 API 和包映射支持

作者&#xff1a;Marco Goertz 排版&#xff1a;Alan Wang .NET 升级助手是一个 Visual Studio 扩展和命令行工具&#xff0c;可帮助您将应用从之前的 .NET 和 .NET Framework 升级到最新版本的 .NET。正如我们在之前的文章中所描述的那样&#xff0c;它为升级 Microsoft 库和框…

vite+vue3创建cesium (ts/js)

在要创建项目的文件夹。输入cmd 1.搭建第一个Vite项目。 npm init vitelatest 安装Cesium插件 cesium插件&#xff1a;vite-plugin-cesium npm i cesium vite-plugin-cesium vite -D配置cesium 在vite.config.ts/vite.config.js文件中 import cesium from vite-plugin-ces…

韦东山嵌入式linux系列-LED 驱动程序框架

1 回顾字符设备驱动程序框架 图中驱动层访问硬件外设寄存器依靠的是 ioremap 函数去映射到寄存器地址&#xff0c;然后开始控制寄存器。 那么该如何编写驱动程序&#xff1f; ① 确定主设备号&#xff0c;也可以让内核分配&#xff1b;② 定义自己的 file_operations 结构体&…