Linux下网络编程(3)——socket编程实战,如何构建一个服务器和客户端连接

news2024/11/24 0:17:09

        经过前几篇的介绍,本文我们将进行编程实战,实现一个简单地服务器和客户端应用程序。

编写服务器程序

        编写服务器应用程序的流程如下:

        ①、调用 socket()函数打开套接字,得到套接字描述符;

        ②、调用 bind()函数将套接字与 IP 地址、端口号进行绑定;

        ③、调用 listen()函数让服务器进程进入监听状态;

        ④、调用 accept()函数获取客户端的连接请求并建立连接;

        ⑤、调用 read()/recv()、write()/send() 与客户端进行通信;

        ⑥、调用 close()关闭套接字。

        下面,我们就根据上面列举的步骤来编写一个简单的服务器应用程序,代码如下所示:

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

#define SERVER_PORT     8888    //端口号不能发生冲突,不常用的端口号通常大于5000

int main(void){
    struct sockaddr_in server_addr = {0};
    struct sockaddr_in client_addr = {0};
    char ip_str[20] = {0};
    int sockfd, connfd;
    int addrlen = sizeof(client_addr);
    char recvbuf[512];
    int ret;

    /* 打开套接字,得到套接字描述符 */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > sockfd) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }

    /* 将套接字与指定端口号进行绑定 */
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERVER_PORT);

    ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (0 > ret) {
        perror("bind error");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    /* 使服务器进入监听状态 */
    ret = listen(sockfd, 50);
    if (0 > ret) {
        perror("listen error");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    /* 阻塞等待客户端连接 */
    connfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen);
    if (0 > connfd) {
        perror("accept error");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("有客户端接入...\n");
    inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str));
    printf("客户端主机的IP地址: %s\n", ip_str);
    printf("客户端进程的端口号: %d\n", client_addr.sin_port);

    /* 接收客户端发送过来的数据 */
    for ( ; ; ) {
        // 接收缓冲区清零
        memset(recvbuf, 0x0, sizeof(recvbuf));

        // 读数据
        ret = recv(connfd, recvbuf, sizeof(recvbuf), 0);
        if(0 >= ret) {
            perror("recv error");
            close(connfd);
            break;
        }
        // 将读取到的数据以字符串形式打印出来
        printf("from client: %s\n", recvbuf);

        // 如果读取到"exit"则关闭套接字退出程序
        if (0 == strncmp("exit", recvbuf, 4)) {
            printf("server exit...\n");
            close(connfd);
            break;
        }
    }
    /* 关闭套接字 */
    close(sockfd);
    exit(EXIT_SUCCESS);
}

        以上我们实现了一个非常简单地服务器应用程序,根据上面列举的步骤完成了这个示例代码,最终的功能是,当客户端连接到服务器之后,客户端会向服务器(也就是本程序)发送数据,在我们服务器应用程序中会读取客户端发送的数据并将其打印出来,就是这么简单的一个功能。

        SERVER_PORT 宏指定了本服务器绑定的端口号,这里我们将端口号设置为 8888,端口不能与其它服务器的端口号发生冲突,不常用的端口号通常大于 5000。

        另外,bind绑定的IP地址是 INADDR_ANY,即表示所有发送到服务器的这个端口,不管是哪个网卡/哪个IP地址接收到的数据,都由这个服务端进程进行处理。

编写客户端程序

        接下来我们再编写一个简单地客户端应用程序,客户端的功能是连接上小节所实现的服务器,连接成功之后向服务器发送数据,发送的数据由用户输入。示例代码如下所示:

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

#define SERVER_PORT		8888          	//服务器的端口号
#define SERVER_IP   	"192.168.1.150"	//服务器的IP地址

int main(void){
    struct sockaddr_in server_addr = {0};
    char buf[512];
    int sockfd;
    int ret;

    /* 打开套接字,得到套接字描述符 */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > sockfd) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }

    /* 调用connect连接远端服务器 */
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);  //端口号
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);//IP地址
    ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (0 > ret) {
        perror("connect error");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("服务器连接成功...\n\n");

    /* 向服务器发送数据 */
    for ( ; ; ) {
        // 清理缓冲区
        memset(buf, 0x0, sizeof(buf));

        // 接收用户输入的字符串数据
        printf("Please enter a string: ");
        fgets(buf, sizeof(buf), stdin);

        // 将用户输入的数据发送给服务器
        ret = send(sockfd, buf, strlen(buf), 0);
        if(0 > ret){
            perror("send error");
            break;
        }

        //输入了"exit",退出循环
        if(0 == strncmp(buf, "exit", 4))
            break;
    }
    close(sockfd);
    exit(EXIT_SUCCESS);
}

        代码不再说明!需要注意的是 SERVER_IP 和 SERVER_PORT 指的是服务器的 IP 地址和端口号,服务器的 IP 地址根据实际情况进行设置,服务器应用程序服务器端代码中我们绑定的端口号为 8888,所以在客户端应用程序中我们也需要指定 SERVER_PORT 为 8888。

编译测试

        这里笔者将服务器程序运行在开发板上,而将客户端应用程序运行在 Ubuntu 系统,当然你也可以将客户端和服务器程序都运行在开发板或 Ubuntu 系统,这都是没问题的。

        首先编译服务器应用程序和客户端应用程序:

         编译得到 client 和 server 可执行文件,server 可执行文件在开发板上运行,client 可执行文件在 PC 端 Ubuntu 系统下运行。将 server 可执行文件拷贝到开发板目录下,如下所示:

         在开发板执行 server:

        接着在 Ubuntu 系统执行客户端程序:

         客户端运行之后将会去连接远端服务器,连接成功便会打印出信息“服务器连接成功...”,此时服务器也会监测到客户端连接,会打印相应的信息,如下所示:

        接下来我们便可以在客户端处输入字符串,客户端程序会将我们输入的字符串信息发送给服务器,服务器接收到之后将其打印出来,如下所示:

Tips : 如果连接出现问题如“connect error: No route to host”,可以参考这篇问题记录开发板和虚拟机socket报错“connect error: No route to host”。

 总结

        到此,socket编程的内容就告一段落,内容讲得非常浅,目的其实并不是让大家学会网络编程,旨在以引导大家入门为主,让大家对 socket 网络编程有一个基本的了解和认识。望诸君常学常新、学思践悟。 

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

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

相关文章

Deep Supervised Dual Cycle Adversarial Network for Cross-Modal Retrieval 论文阅读笔记

​# 1. 论文信息 论文名称Deep Supervised Dual Cycle Adversarial Network for Cross-Modal Retrieval作者Lei Liao 中山大学会议/出版社IEEE Transactions on Circuits and Systems for Video Technologypdf&#x1f4c4;在线pdf代码&#x1f4bb;无代码 本文是基于公共空间…

mov转换为mp4格式怎么转,多方法教程

mov转换为mp4格式怎么转&#xff1f;如果你连mov都不了解&#xff0c;那就更别说将其进行转换了。其实使用过苹果手机的人应该接触的很多&#xff0c;但是我们时常不关注这个视频格式。MOV是一种音频和视频文件格式&#xff0c;它在苹果手机上使用。它不兼容安卓或Windows电脑&…

cookie、session、JWT(Json Web Token) 的区别?

cookie、session、JWT(Json Web Token) 的区别&#xff1f; 答&#xff1a; 要从http的无状态说起&#xff0c;http是无状态的&#xff0c;也就是如果你第一次访问服务器&#xff0c;之后再次访问的时候&#xff0c;服务器是不会意识到你再次来进行访问的。不想让已经登录的用…

敏捷ACP 常用关键词整理 敏捷ACP 常用知识点整理

敏捷ACP 常用关键词整理 敏捷ACP 常用知识点整理 一、MoSCoW 1、MoSCoW &#xff1a; 读作"莫斯科"&#xff0c;适用于故事优先级的排序&#xff0c;首次出现在 3-13敏捷产品实践&#xff1a;产品待办事项列表的排序 &#xff1b; 基于价值的分析的一种技术 &#…

mac python3.9安装pyqt5、qt5、pyqt5-tools

一 pip安装 转义安装 # 一条代码就可以搞定没错&#xff0c;使用的是Rosetta2 x86_64模式安装的 arch -x86_64 python3 -m pip install PyQt5arch -x86_64 python3 -m pip install pyqt5-tools二 brew安装 arm版 以下pip命令自行更具自己环境选择pip或pip3 在安装pyqt前必须先…

【C++】set和map的底层AVL树的实现

AVL树 文章目录 前言一、AVL树的实现总结 前言 上一篇文章对 map/multimap/set/multiset 进行了简单的介绍&#xff0c;在其文档介绍中发现&#xff0c;这几个容器有个共同点是&#xff1a;其底层都是按照二叉搜索树来实现的 &#xff0c;但是二叉搜索树有其自身的缺陷&#xf…

OC消息机制

目录 1 OC消息机制2 OC消息发送3 OC动态方法解析4 OC消息转发 1 OC消息机制 OC对象调用方法在编译阶段不知道具体的方法在哪里&#xff0c;是在运行的过程中&#xff0c;向对象发送消息&#xff0c;通过对象得到函数地址&#xff0c;调用函数&#xff0c;如果没有找到&#xf…

计算机网络实验(ensp)-实验 9:配置 NAT 及了解 ping 命令

目录 实验报告&#xff1a; 实验操作 1.建立网络拓扑图并开启设备 2.配置路由器 1.输入命名&#xff1a;sys 从用户视图切换到系统视图 2.输入命名&#xff1a;sysname 姓名 修改路由器名字 3.输入命名&#xff1a;interface g0/0/1 &#xff08;简写&…

Linux——进程和计划任务管理

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。座右铭&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石&#xff0c;故能成其高。 个人主页&#xff1a;小李会科技的…

Uniapp申请APP版应用签名、打包、并在微信开放平台申请移动应用的全流程攻略

一.应用签名申请 1.1 安装jdk, jre, 并设置环境变量 Jdk下载链接 1.2 准备就绪后, 重新打开cmd窗口, 键入Java看是否已正确配置.** 1.3 如已正确配置,则执行以下命令,进行证书生成操作。生成好的证书保存在当前cmd的启动目录下** 证书别名和应用名称可替换成自己的软件名称.…

【容斥+状压+树上异或】ABC152 F - Tree and Constraints

思路真的和他很像&#xff0c;但是我不太会写容斥&#xff0c;只写过几道板子题 而且有个地方不知道怎么处理&#xff0c;就是容斥原理的F函数里面怎么求多个路径的边的并集 这里是用状压处理的 该学学容斥的写法了QwQ F - Tree and Constraints (atcoder.jp) 题意&#x…

Python 实验六 函数的设计

1.编写两个函数分别按单利和复利计算利息&#xff0c;根据本金、年利率、存款年限得到本息和和利息。调用这两个函数计算1000元在银行存3年&#xff0c;在年利率是6%的情况下&#xff0c;单利和复利分别获得的本息和和利息。单利计算指只有本金计算利息。复利计算是指不仅本金计…

【问题解决】Mybatis Plus Generator(新代码生成器)+达梦数据库,创建数据库连接失败

前言 使用Mybatis Plus Generator&#xff08;新代码生成器&#xff09;达梦数据库&#xff0c;执行生成方法失败 无法创建数据库连接 文章目录 前言问题描述错误日志输出尝试解决最终解决方法 问题描述 创建数据源配置&#xff0c;连接配置参照官网 jdbc:dm://localhost:5236/…

电脑照片怎么导入苹果手机?三个妙招帮你解决!

案例&#xff1a;电脑有很多照片&#xff0c;该如何导入苹果手机&#xff1f; 【家人们&#xff0c;电脑里面的照片怎么样可以快速导入到苹果手机&#xff1f;求方法&#xff01;】 导入电脑照片到苹果手机是一个常见的需求&#xff0c;尤其是当您希望在手机上随时欣赏和分享这…

从桌面端到移动端,.NET MAUI为什么对WPF开发人员更简单?

.NET多平台应用程序UI&#xff08;. NET MAUI&#xff09;的市场吸引力与日俱增&#xff0c;这是微软最新的开发平台&#xff0c;允许开发者使用单个代码库创建跨平台应用程序。尽管很多WPF开发人员还没有跟上 .NET MAUI的潮流&#xff0c;但我们将在这篇文章中为大家展示他的潜…

数字信号处理基础(一)

目录 1. 写在前面2. 连续信号和离散信号2.1连续信号2.2 离散信号 3. 常用信号的产生3.1 单位脉冲序列3.2 单位阶跃序列3.3 指数函数3.4 正弦信号和余弦信号3.5 sinc函数3.6 矩形脉冲信号 4. 信号卷积5. 完整代码 1. 写在前面 为了更好的理解通信原理系列文章&#xff0c;在此补…

说说什么是IO多路复用?以及其演进过程。

文章目录 1.阻塞IO模型&#xff08;BIO&#xff09;和 非塞IO模型&#xff08;NIO&#xff09;2.什么是IO多路复用&#xff1f;3.IO多路复用的演进&#xff1f; 1.阻塞IO模型&#xff08;BIO&#xff09;和 非塞IO模型&#xff08;NIO&#xff09; 阻塞IO模型&#xff08;BIO&…

国考省考行测:科学推理,光学,声学

国考省考行测&#xff1a; 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡申论和行测的重要知识点 遇到寒冬&am…

响应式UI组件库Kendo UI for Vue可轻松创建联系表单!

Kendo UI致力于新的开发&#xff0c;来满足不断变化的需求。Kendo UI for Vue使用旨在提高性能和丰富用户体验的Vue组件&#xff0c;帮助开发人员构建下一代应用程序。它是为Vue技术框架提供可用的Kendo UI组件&#xff0c;以便更快地构建更好的Vue应用程序。 联系表单用于在网…

CRM系统的实施要注意什么?

CRM系统的实施要注意什么&#xff1f; 在如今竞争激烈的商业环境中&#xff0c;拥有一个高效的CRM系统已经变成了企业不可或缺的一部分。 这就来讲一讲&#xff0c;在实施CRM过程中需要注意哪些问题。 01 CRM实施要注意哪些问题&#xff1f; 企业在实施CRM时&#xff0c;需…