Linux系统下建立Socket聊天服务器

目录

1.服务器结构

2.各模块函数

2.1 socket函数 

2.2 bind函数

2.3 Listen函数

2.4 accept函数

2.5 接收发送函数

2.6 close函数

2.7 connect函数

3 代码段

3.1 服务器代码


1.服务器结构

使用socket的API函数编写服务端和客户端程序的步骤图示:

2.各模块函数

服务器:

2.1 socket函数 

使用socket会建立一个服务器文件描述符

  • 成功: 返回一个大于0的文件描述符
  • 失败: 返回-1, 并设置errno
int socket(int domain, int type, int protocol);

domain: 协议版本

AF_INET IPV4
AF_INET6 IPV6
AF_UNIX AF_LOCAL本地套接字使用

type:协议类型

SOCK_STREAM 流式, 默认使用的协议是TCP协议
SOCK_DGRAM  报式, 默认使用的是UDP协议

protocal:

一般填0, 表示使用对应类型的默认协议.

2.2 bind函数

成功: 返回0
失败: 返回-1, 并设置errno

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

函数描述: 将socket文件描述符和IP,PORT绑定。

sockfd为socket的返回值,文件描述符

struct sockaddr* addr结构体可以用下面的

struct sockaddr_in serv;
serv.sin_family = AF_INET;//选择使用的网络协议
serv.sin_port = htons(8888);//绑定本机端口,通常占2字节。注意:端口号尽量不要填1024以前的数字,因为可以被系统预留了。
serv.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY: 表示使用本机任意有效的可用IP
如果想自己指定ip地址作为服务器连接就需要这个:
inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
或者inet_aton("127.0.0.1", &serv.sin_addr);
或者这个:addr.sin_addr.s_addr = inet_addr("192.168.239.1");

 同时在使用addr时,先对其进行清空memset。

“端口号所谓的端口,就好像是门牌号一样,客户端可以通过ip地址找到对应的服务器端,但是服务器端是有很多端口的,每个应用程序对应一个端口号,通过类似门牌号的端口号,客户端才能真正的访问到该服务器。为了对端口进行区分,将每个端口进行了编号,这就是端口号。”
  你可能对出现的htons()、htonl和inet_pton()不知道是何意,在网络传输中,不同的机器端不一样,有的机器是大端有的机器是小端。这些函数是为了帮助你在传输网络数据的时候统一格式。(没有超过一个字节不需要转)

大端: 低位地址存放高位数据, 高位地址存放低位数据(也叫网络字节序)
小端: 低位地址存放低位数据, 高位地址存放高位数据(也叫小端字节序)

网络中传输使用的是大端法,如果机器使用的是小端,则需要进行大小端的转换。
  下面4个函数就是进行大小端转换的函数:

  #include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);uint16_t htons(uint16_t hostshort);uint32_t ntohl(uint32_t netlong);uint16_t ntohs(uint16_t netshort);

函数名的h表示主机host, n表示网络network, s表示short, l表示long
上述的几个函数, 如果本来不需要转换函数内部就不会做转换.

IP地址转换函数:

p->表示点分十进制的字符串形式
to->到
n->表示network网络

int inet_pton(int af, const char *src, void *dst);

函数说明: 将字符串形式的点分十进制IP转换为大端模式的网络IP(整形4字节数)
参数说明:

af: AF_INET
src: 字符串形式的点分十进制的IP地址
dst: 存放转换后的变量的地址

如192.168.232.145, 先将4个正数分别转换为16进制数,
  192—>0xC0 168—>0xA8 232—>0xE8 145—>0x91
  最后按照大端字节序存放: 0x91E8A8C0, 这个就是4字节的整形值.

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);


函数说明: 网络IP转换为字符串形式的点分十进制的IP
参数说明:

af: AF_INET
src: 网络的十六进制的IP地址
dst: 转换后的IP地址,一般为字符串数组
size: dst的长度
成功--返回执行dst的指针
失败--返回NULL, 并设置errno

如 IP地址为010aa8c0, 转换为点分十进制的格式:
  01---->1 0a---->10 a8---->168 c0---->192
  由于从网络中的IP地址是高端模式, 所以转换为点分十进制后应该为: 192.168.10.1

2.3 Listen函数

int listen(int sockfd, int backlog);

成功: 返回0 失败: 返回-1, 并设置errno 

函数描述: 将套接字由主动态变为被动态,也就是设置为监听文件描述符。

参数说明:

sockfd: 调用socket函数返回的文件描述符
backlog: 同时请求连接的最大个数(还未建立连接)  设置为6/7注意:在linux系统中,这里代表全连接队列(已连接队列)的数量。在unix系统种,这里代表全连接队列(已连接队列)+ 半连

2.4 accept函数

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

函数说明:获得一个连接, 若当前没有连接则会阻塞等待.

sockfd: 调用socket函数返回的文件描述符
addr: 传出参数, 保存客户端的地址信息。如果不关心可以传NULL。
addrlen: 传入传出参数,  addr变量所占内存空间大小,这个传出的时候会告诉我们填充的多少的内容。如果不关心可以传NULL。

成功: 返回一个新的文件描述符,用于和客户端通信             失败: 返回-1, 并设置errno值.

accept函数是一个阻塞函数, 若没有新的连接请求, 则一直阻塞。从已连接队列中获取一个新的连接, 并获得一个新的文件描述符, 该文件描述符用于和客户端通信. (内核会负责将请求队列中的连接拿到已连接队列中)。

2.5 接收发送函数

接下来就可以使用write和read函数进行读写操作了。除了使用read/write函数以外, 还可以使用recv和send函数。

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);    
//对应recv和send这两个函数flags直接填0就可以了.

fd为accept返回的fd,count为字节,flag写0

注意: 如果写缓冲区已满, write也会阻塞, read读操作的时候, 若读缓冲区没有数据会引起阻塞.
 

2.6 close函数

  最后通讯完之后记得close()文件描述符,关闭文件描述符后就断开了连接,就从已连接队列里面去掉了.。

2.7 connect函数

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

函数说明: 连接服务器,client.c使用connect函数前应该先使用socket函数得到文件描述符fd。
函数参数:

addr设置为服务端一样的就行,进行传入

sockfd: 调用socket函数返回的文件描述符
addr: 服务端的地址信息
addrlen: addr变量的内存大小   用sizeof

返回值:

成功: 返回0
失败: 返回-1, 并设置errno值

主要用于客户端连接,客户端不需要绑定端口、ip什么的,因为只要能连上然后传输接收数据就行。
然后直接用sockfd进行读写就行。

3 代码段

3.1 服务器代码

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>int main(void)
{int s_fd,ss_fd,nread,len;char buf[32];char msg[32];struct sockaddr_in s_ddr;  //build server msgstruct sockaddr_in c_ddr;  //save clinet msgs_fd= socket(AF_INET, SOCK_STREAM, 0);//1.build a soket specifiedif(s_fd==-1){perror("error is");}//2.build all binds_ddr.sin_family=AF_INET;s_ddr.sin_port=htons(8880);s_ddr.sin_addr.s_addr=htonl(INADDR_ANY);//give the bindbind(s_fd,(struct sockaddr *)&s_ddr,sizeof(s_ddr));//3.waite for clientlisten(s_fd,8);//4.accept come and connect for oncelen=sizeof(c_ddr);while(1){                        //这里用while1是为了一直可以被连接ss_fd=accept(s_fd,(struct sockaddr *)&c_ddr,&len);  printf("conect succese!==========\r\n");//5.read from connect ss_fdif(fork()==0){               //创建一个子进程(服务员)去接待clientif(fork()==0){  //fork is zero is child pid   //创建一个子进程去等待发送//5.1  sendwhile(1){memset(msg,0,32);	printf("input:");gets(msg);send(ss_fd,msg,32,0);}}//5.2 read	                                    //在父进程中等待接收数据while(1){memset(buf,'\0',32);nread=read(ss_fd,&buf,32);printf("server  receved :%s \r\n",buf); }}}close(ss_fd);close(s_fd);return 0;
}

3.2 客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>int main(int argc,char *argv[])
{int flag,s_fd,n_read;struct sockaddr_in c_ddr;char readbuf[32];char msg[32];//1.build sockets_fd=socket(AF_INET,SOCK_STREAM,0);//2.0 prepare server addrmemset(&c_ddr,0,sizeof(c_ddr)); //clear c_ddrc_ddr.sin_family=AF_INET;c_ddr.sin_port=htons(8880);inet_aton("192.168.102.141",&c_ddr.sin_addr);//2.connect server get s_fdif(connect(s_fd,(struct sockaddr *)&c_ddr,sizeof(c_ddr))==-1){perror("error");}printf("connect success==============\r\n");while(1){                                      //while1父进程一直等待读数据        //recv  will block  memset(readbuf,0,32);read(s_fd,readbuf,32);printf("form server:%s\r\n",readbuf);//sendif(fork()==0){  //fork is zero is child pid   //子进程一直(while1)等待发数据while(1){memset(msg,0,32);printf("input :::::");gets(msg);send(s_fd,msg,32,0);}}}close(s_fd);return 0;
} 

参考博文:

Linux环境下socket服务器搭建_socket搭建linux_master cat的博客-CSDN博客

socket编程 服务器_socket 服务器_不爱学习的王小二的博客-CSDN博客

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

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

相关文章

循环购模式:美妆行业的新趋势

美妆是一种能够提升自信和魅力的艺术&#xff0c;它让每个人都可以展现自己的个性和风格。但是&#xff0c;美妆也是一种需要不断更新和学习的技能&#xff0c;它需要消费者投入时间和金钱&#xff0c;才能找到适合自己的产品和方法。有没有一种方式&#xff0c;可以让美妆变得…

MySQL 数据库常用命令大全(详细)

文章目录 1. MySQL命令2. MySQL基础命令3. MySQL命令简介4. MySQL常用命令4.1 MySQL准备篇4.1.1 启动和停止MySQL服务4.1.2 修改MySQL账户密码4.1.3 MySQL的登陆和退出4.1.4 查看MySQL版本 4.2 DDL篇&#xff08;数据定义&#xff09;4.2.1 查询数据库4.2.2 创建数据库4.2.3 使…

[Linux]进程程序替换

[Linux]进程程序替换 文章目录 [Linux]进程程序替换进程程序替换的意义见一见进程程序替换进程程序替换的原理进程程序替换中的写时拷贝介绍进程程序替换接口 进程程序替换的意义 Linux系统下使用fork系统函数创建子进程后&#xff0c;子进程只能执行继承的部分父进程代码&…

cocos creator配置终端调试

在launch.json里添加"preLaunchTask":“CocosCreator compile” 在cocos creator里选择开发者&#xff0c;visual studio code工作流&#xff0c;选择添加编译任务。 添加 settings.json {"files.exclude":{"**/.git": true,"**/.DS_Sto…

哈希表HashMap(基于vector和list)

C数据结构与算法实现&#xff08;目录&#xff09; 1 什么是HashMap&#xff1f; 我们这里要实现的HashMap接口不会超过标准库的版本&#xff08;是一个子集&#xff09;。 HashMap是一种键值对容器&#xff08;关联容器&#xff09;&#xff0c;又叫字典。 和其他容易一样…

FFDNet-pytorch版本代码训练教程

一、FFDNet-pytorch版本代码下载 (1)FFDNet-pytorch下载 https://download.csdn.net/download/qq_41104871/88233742 (2)FFDNet-pytorch版本代码运行环境配置 https://blog.csdn.net/qq_41104871/article/details/132497008 二、FFDNet-pytorch版本代码训练教程 (1)按…

Elasticsearch 7.6 - Springboot应用基础操作篇

ES 7.6 - JAVA应用基础操作篇 环境准备依赖配置 实体类准备使用说明索引/映射操作创建索引和映射索引和映射相关查询删除索引 文档操作插入数据更新数据删除数据批量操作 文档查询根据ID查询根据字段精准查询根据字段分词查询控制返回字段范围查询组合查询排序分页高亮搜索聚合…

大数据平台数据脱敏是什么意思?有哪些方案?

大数据平台包含了海量多样化数据&#xff0c;所以保障大数据平台数据安全非常重要&#xff0c;数据脱敏就是手段之一。今天我们就来简单聊聊大数据平台数据脱敏是什么意思&#xff1f;有哪些方案&#xff1f; 大数据平台数据脱敏是什么意思&#xff1f; 大数据平台数据脱敏简…

matlab使用教程(27)—微分代数方程(DAE)求解

1.什么是微分代数方程&#xff1f; 微分代数方程是一类微分方程&#xff0c;其中一个或多个因变量导数未出现在方程中。方程中出现的未包含其导数的变量称为代数变量&#xff0c;代数变量的存在意味着您不能将这些方程记为显式形式 y ′ f t , y 。相反&#xff0c;您可以…

安全学习DAY20_自动化工具项目武器库介绍

信息打点-自动化工具 文章目录 信息打点-自动化工具本节思维导图&概述 各类红蓝队优秀工具项目集合&#xff1a;All-Defense-Tool 自动化-武器库部署F8x 自动化信息搜集-网络空间AsamF 自动化信息搜集-企查信息ENScan 自动化信息搜集-综合架构-ARL&NemoARL灯塔Nemo_Go …

javaCV实现java图片ocr提取文字效果

引入依赖&#xff1a; <dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.5</version></dependency> 引入中文语言训练数据集&#xff1a;chi_sim GitHub - tesseract-ocr…

网络综合布线专业技能人才培养方案

一、网络综合布线专业技能人才培养方案概述 网络综合布线专业技能人才培养方案旨在培养具备综合布线系统设计、安装与维护的全面技能和知识的专业人才。以下是一个概述&#xff1a; 培养目标&#xff1a;培养具备综合布线系统的理论基础和实践操作技能的专业人才&#xff0c;能…

使用postman做接口测试

1.接口测试&#xff1a;针对软件对外提供服务的接口的输入输出进行测试&#xff0c;以及接口间相互逻辑的测试&#xff0c;验证接口功能与接口描述文档的一致性 2.接口测试流程&#xff1a; 1&#xff09;获取接口信息&#xff1a;通过接口文档或抓包来获取接口的基本调用方式和…

uniapp 微信小程序 锚点跳转

uniapp文档 以下是我遇到的业务场景&#xff0c;是点击商品分类的某一类 然后页面滚动至目标分类&#xff0c; 首先第一步是设置锚点跳转的目的地&#xff0c;在目标的dom上面添加id属性 然后给每个分类每一项添加点击事件&#xff0c;分类这里的item数据里面有一字段是和上…

SpringBoot初级开发--多环境配置的集成(9)

在Springboot的开发中&#xff0c;我们经常要切换各种各样的环境配置&#xff0c;比如现在是开发环境&#xff0c;然后又切换到生产环境&#xff0c;这个时候用多环境配置就是一个明智的选择。接下来我们沿用上一章的工程来配置多环境配置工程。 1.准备多环境配置文件 这里我…

Flutter 安装教程 + 运行教程

1.下载依赖 https://flutter.cn/docs/get-started/install/windows 解压完后根据自己的位置放置&#xff0c;如&#xff08;D:\flutter&#xff09; 注意 请勿将 Flutter 有特殊字符或空格的路径下。 请勿将 Flutter 安装在需要高权限的文件夹内&#xff0c;例如 C:\Program …

FireFox禁用HTTP2

问题 最近需要调试接口&#xff0c;但是&#xff0c;Chrome都是强制使用h2协议&#xff0c;即HTTP/2协议。为了排除h2协议排除对接口调用的影响&#xff0c;需要强制浏览器使用HTTP1协议。 解决 FireFox 设置firefox的network.http.http2.enabled为禁用&#xff0c;这样就禁…

[ros][ubuntu]ros在ubuntu18.04上工作空间创建和发布一个话题

构建catkin工作空间 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src catkin_init_workspace cd ~/catkin_ws/ catkin_make 配置环境变量 echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc source ~/.bashrc 检查环境变量 echo $ROS_PACKAGE_PATH…

【NVIDIA CUDA】2023 CUDA夏令营编程模型(二)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

Vue3 select循环多个,选项option不能重复被选

Vue3 select循环多个&#xff0c;选项option不能重复被选 环境&#xff1a;vue3tsviteelement plus 实现目标&#xff1a;Vue3 select循环多个&#xff0c;当其中一个option值被选后&#xff0c;其他select里面不能再重复选择该option值。第二种&#xff0c;当其中一个option值…