socket编程(2) -- TCP通信

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等技术内容,点击立即学习:链接

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Excel第30享:基于辅助列的条件求和

1、需求描述 如下图所示&#xff0c;现要统计2022年YTD&#xff08;Year To Date&#xff1a;年初至今日&#xff09;各个人员的“上班工时&#xff08;a2&#xff09;”。 下图为系统直接导出的工时数据明细样例。 2、解决思路 Step1&#xff1a;确定逻辑。“从日期中提取出…

[spring] Spring MVC - security(上)

[spring] Spring MVC - security&#xff08;上&#xff09; 这部分的内容基本上和 [spring] rest api security 是重合的&#xff0c;主要就是添加 验证&#xff08;authentication&#xff09;和授权&#xff08;authorization&#xff09;这两个功能 即&#xff1a; 用户…

构造函数的初始化列表,static成员,友元,内部类【类和对象(下)】

P. S.&#xff1a;以下代码均在VS2022环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;LiUEEEEE                        …

2-31 基于matlab的微表情识别

基于matlab的微表情识别。通过gabor小波提取表情特征&#xff0c;pca进行降维&#xff0c;ELM分类器训练&#xff0c;然后选择待识别的微表情&#xff0c;提取特征后输入训练好的模型进行分类&#xff0c;识别结果由MATLAB的GUI输出。程序已调通&#xff0c;可直接运行。 2-31 …

Tomcat多实例

一、Tomcat多实例 Tomcat多实例是指在同一台服务器上运行多个独立的tomcat实例&#xff0c;每个tomcat实例都具有独立的配置文件、日志文件、应用程序和端口&#xff0c;通过配置不同的端口和文件目录&#xff0c;可以实现同时运行多个独立的Tomcat服务器&#xff0c;每个服务…

Fastjson2使用JSONOObject或者mao转换为JSON字符串时丢失Null值字段

最近在工作中发现问题fastJson转换为JSONString时丢失值为null的问题特此解决。 public class test001 {public static void main(String[] args) {JSONObject jsonObject new JSONObject();jsonObject.put("foo1", "bar");jsonObject.put("foo2&quo…

19. 地址转换

地址转换 题目描述 Excel 是最常用的办公软件。每个单元格都有唯一的地址表示。比如&#xff1a;第 12 行第 4 列表示为&#xff1a;"D12"&#xff0c;第 5 行第 255 列表示为"IU5"。 事实上&#xff0c;Excel 提供了两种地址表示方法&#xff0c;还有一…

代码随想录第50天|单调栈

739. 每日温度 参考 思路1: 暴力解法 思路2: 单调栈 使用场合: 寻找任一个元素的右边或者左边第一个比自己大或者小的元素位置, 存放的是遍历过的元素 记忆: 单调栈是对遍历过的元素做记录, 一般是对栈顶的元素 nums[mystack.top()] 做赋值操作的 如果想找到右边的元素大于左…

Efficient Estimation of Word Representations in Vector Space论文笔记解读

基本信息 作者TomasMikolovdoi10.48550发表时间2013期刊ICLR网址http://arxiv.org/abs/1301.3781 研究背景 1. What’s known 既往研究已证实 前馈神经网络语言模型(NNLM) 循环神经网络语言模型(RNNLM) 2. What’s new 创新点 Word2vec有两种模型&#xff1a;CBOW和Skip-gr…

【区块链 + 智慧政务】一体化政务数据底座平台 | FISCO BCOS应用案例

为进一步贯彻落实《全国一体化政务大数据体系建设方案》、《中共中央国务院关于构建数据基础制度更好发挥 数据要素作用的意见》精神&#xff0c;一体化政务数据底座平台结合相应城市的数字经济现状基础、当前任务及未来发展 战略&#xff0c;规划建设数据底座&#xff0c;持续…

Qt QWebSocket网络编程

学习目标&#xff1a;Qt QWebSocket网络编程 学习前置环境 QT TCP多线程网络通信-CSDN博客 学习内容 WebSocket是一种通过单个TCP连接提供全双工通信信道的网络技术。2011年&#xff0c;IETF将WebSocket协议标准化为 RFC6455&#xff0c;QWebSocket可用于客户端应用程序和服…

社区团购小程序源码系统 带完整的安装代码以及搭建部署教程

系统概述 在这个数字化时代&#xff0c;线上活动成为了连接用户与组织者的桥梁。为了满足不同场景的需要&#xff0c;开发一个灵活、可定制的在线活动报名表单小程序显得尤为重要。本文将深入介绍一个自定义在线活动报名表单小程序的源码系统&#xff0c;并提供详细的搭建部署…

【JavaScript 算法】快速排序:高效的排序算法

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、算法原理二、算法实现三、应用场景四、优化与扩展五、总结 快速排序&#xff08;Quick Sort&#xff09;是一种高效的排序算法&#xff0c;通过分治法将数组分为较小的子数组&#xff0c;递归地排序子数组。快速排序通常…

vue使用quill编辑器自定义附件上传方法,并根据上传附件名称生成链接

1、附件上传 需求&#xff1a; 在编辑器中上传word,pdf,excel等附件后&#xff0c;能根据上传附件的名称生成link链接&#xff0c;在展示页面能实现点击链接下载或预览附件&#xff0c;效果图如下: 实现方法&#xff1a; quill编辑器自身带有link&#xff0c;但不满足需求&…

Java---SpringBoot详解二

勤奋勤劳铸梦成&#xff0c; 晨曦微露起长征。 汗水浇灌花似锦&#xff0c; 寒窗苦读岁月明。 千锤百炼心如铁&#xff0c; 万里征途志不倾。 持之以恒终有日&#xff0c; 功成名就笑谈中。 目录 一&#xff0c;统一响应结果 二&#xff0c;三层架构 三&#xff0c;分层解耦 四…

基于html开发的在线网址导航在线工具箱源码

基于html开发的在线网址导航在线工具箱源码&#xff0c;将全部文件复制到服务器&#xff0c;入口文件是index.html 如需修改网址&#xff0c;可修改index.html 如需修改关于页面&#xff0c;可修改about里面的index页面 源码下载&#xff1a;https://download.csdn.net/down…

存储实验:Linux挂载iscsi硬盘与华为OceanStor创建LUN全流程

目录 目的环境规划实验实验流程Centos配置0. 关闭防火墙1. 设置网卡信息2. 配置路由3. iscsiadm连接存储 iSCSI LUN创建&#xff08;以华为OceanStor为例&#xff09;验证1. 验证是否成功2. 开启自动挂载 目的 实现Linux连接iscsi硬盘&#xff0c;同时实现开机自启挂载 环境规…

综合实验作业

node01&#xff1a;192.168.175.146 node02&#xff1a;192.168.175.147 【node01】 node01 与 node02 防火墙在本实验中都需要放行的服务&#xff1b; [rootlocalhost ~]# firewall-cmd --permanent --add-servicedns success [rootlocalhost ~]# firewall-cmd --permanent -…

实变函数精解【3】

文章目录 点集求导集 闭集参考文献 点集 求导集 例1 E { 1 / n 1 / m : n , m ∈ N } 1. lim ⁡ n → ∞ ( 1 / n 1 / m ) 1 / m 2. lim ⁡ n , m → ∞ ( 1 / n 1 / m ) 0 3. E ′ { 0 , 1 , 1 / 2 , 1 / 3 , . . . . } E\{1/n1/m:n,m \in N\} \\1.\lim_{n \rightar…

PGCCC|【PostgreSQL】PCA认证考试大纲#postgresql认证

PostgreSQL Certified Associate|PCA&#xff08;初级&#xff09; 学员将学会安装、创建和维护PostgreSQL数据库。学完后&#xff0c;学员可以从事PostgreSQL数据库的数据操作和管理等工作。 获证途径 参加PostgreSQL培训再考试 考试为上机考试。 PostgreSQL PCA培训考试课…