【Linux网络编程】01:Socket多进程
创始人
2024-06-03 22:53:01
0

Socket多进程


OVERVIEW

  • Socket多进程
        • 1.Server
        • 2.Client
        • 3.bug&rethink
        • 4.source code

调用socket、connect、bind、listen、accept、send、recv

在这里插入图片描述

1.Server

//1.server.c
#include "head.h"
#include "common.h"#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int main(int argc, char *argv[]) {//./a.out -p port//1.命令行解析if (argc != 3) {fprintf(stderr, "Usage : %s -p port", argv[0]);exit(1);}int opt;int port;int sockfd;while ((opt = getopt(argc, argv, "p:")) != -1) {switch (opt) {case 'p':port = atoi(optarg);break;default:fprintf(stderr, "Usage : %s -p port\n", argv[0]);exit(1);}}//2.创建socketif ((sockfd = socketCreate(port)) < 0) handle_error("socketCreate");//3.accept循环的接受客户端对server的连接while (1) {int newfd;if ((newfd = accept(sockfd, NULL, NULL)) < 0) handle_error("accept");//不关注客户端的ip地址与端口号NULLprintf(" : accept a client!\n");//4.recv接受到消息while (1) {char buff[1024] = {0};ssize_t rsize = recv(newfd, buff, sizeof(buff), 0);if (rsize > 0) printf(" : %s\n", buff);}}return 0;
}
//common.h
#ifndef _COMMON_H
#define _COMMON_Hint socketCreate(int port);#endif
//common.c
#include "head.h"int socketCreate(int port) {//1.创建套接字int sockfd;struct sockaddr_in addr;if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1;addr.sin_family = AF_INET;addr.sin_port = htons(port);//主机字节序转换为网络字节序addr.sin_addr.s_addr = inet_addr("0.0.0.0");//将网络字节序转化为主机字节序 0.0.0.0表示不关注消息传来的地址//2.bind绑定套接字与结构体信息 listenif (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) return -1;if (listen(sockfd, 20) < 0) return -1;return sockfd;
} 

将2000端口作为服务端绑定的端口,启动服务端程序

在这里插入图片描述

新建一个窗口利用netstat -alnt命令查看已经在使用的端口及其当前状态:LISTEN

在这里插入图片描述

利用telnet ip port命令对服务端进行测试(需要开放阿里云服务器安全组,并设置linux系统的防火墙才能进行测试):

在这里插入图片描述

服务端接收到消息并receive打印输出消息:

在这里插入图片描述

可以看到服务端成功接收到消息,并打印了消息内容。

2.Client

#include "head.h"#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int main(int argc, char *argv[]) {//./a.out ip portif (argc != 3) {fprintf(stderr, "Usage : %s ip port\n", argv[0]);exit(1);}//1.客户端打开一个socketint sockfd;if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) handle_error("socket");//2.定义结构体用于绑定端口号、ip地址(存放服务端的具体信息)struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(atoi(argv[2]));//端口号server.sin_addr.s_addr = inet_addr(argv[1]);//ip地址//3.建立连接connectionif (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) handle_error("connect");//4.send发送数据while (1) {//循环发送消息char buff[1024] = {0};scanf("%[^\n]s", buff);//输入可含空格的字符串getchar();//吞掉一个换行//只要向文件描述符中写入 tcp服务就会帮助发送消息//With a zero flags argument, send is equivalent to write(2).if (strlen(buff)) send(sockfd, buff, sizeof(buff), 0);}return 0;
}

同时启动客户端与服务端,从客户端发送消息,可以看到服务端能够正常接收消息并打印:

在这里插入图片描述

3.bug&rethink

程序还有几个功能上没有处理的bug:

  1. 客户端ctrl+C结束之后,服务端无法接收到客户端退出连接的状态。
  2. 如果创建多个客户端尝试连接服务端,服务端将无法接收到客户端的连接请求。
  3. 服务端能够知道是谁向我发送的数据(打印出客户端的ip地址和端口号)

问题1解决:在client.c中利用signal信号,并添加一个closeSock函数对客户端ctrl+c后向服务端做出相应:

#include "head.h"#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int sockfd;//ctrl+c信号处理
void closeSock(int signum) {send(sockfd, " : I am leaving...", 27, 0);close(sockfd);exit(1);
}int main(int argc, char *argv[]) {//./a.out ip portif (argc != 3) {fprintf(stderr, "Usage : %s ip port\n", argv[0]);exit(1);}//1.客户端打开一个socketif ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) handle_error("socket");//2.定义结构体用于绑定端口号、ip地址(存放服务端的具体信息)struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(atoi(argv[2]));//端口号server.sin_addr.s_addr = inet_addr(argv[1]);//ip地址signal(SIGINT, closeSock);//3.建立连接connectionif (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) handle_error("connect");printf("connect sccuess!\n");//4.send发送数据while (1) {//循环发送消息char buff[1024] = {0};scanf("%[^\n]s", buff);//输入可含空格的字符串getchar();//吞掉一个换行//只要向文件描述符中写入 tcp服务就会帮助发送消息//With a zero flags argument, send is equivalent to write(2).if (strlen(buff)) send(sockfd, buff, sizeof(buff), 0);}return 0;
}

问题2解决:在server.c中fork多个进程,父进程用于accept、子进程用于receive并将接受到的信息进行打印。

#include "head.h"
#include "common.h"#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int main(int argc, char *argv[]) {//./a.out -p port//1.命令行解析if (argc != 3) {fprintf(stderr, "Usage : %s -p port", argv[0]);exit(1);}int opt;int port;int sockfd;while ((opt = getopt(argc, argv, "p:")) != -1) {switch (opt) {case 'p':port = atoi(optarg);break;default:fprintf(stderr, "Usage : %s -p port\n", argv[0]);exit(1);}}//2.创建socketif ((sockfd = socketCreate(port)) < 0) handle_error("socketCreate");//3.accept循环的接受客户端对server的连接while (1) {int pid;int newfd;//新建的文件描述符if ((newfd = accept(sockfd, NULL, NULL)) < 0) handle_error("accept");//不关注客户端的ip地址与端口号NULLprintf(" : accept a client!\n");//4.子进程recv接受到消息if ((pid = fork()) < 0) handle_error("fork");if (pid == 0) {//子进程进行操作while (1) {char buff[1024] = {0};ssize_t rsize = recv(newfd, buff, sizeof(buff), 0);if (rsize > 0) {printf(" : %s\n", buff);} else {close(sockfd);break;}}printf(" : a client left!\n");exit(0);}}return 0;
}

在这里插入图片描述

问题3解决:在server.c中对accept函数位置进行修改,获取到client端的具体信息显示并打印在聊天的过程中:

#include "head.h"
#include "common.h"#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int main(int argc, char *argv[]) {//./a.out -p port//1.命令行解析if (argc != 3) {fprintf(stderr, "Usage : %s -p port", argv[0]);exit(1);}int opt;int port;int sockfd;while ((opt = getopt(argc, argv, "p:")) != -1) {switch (opt) {case 'p':port = atoi(optarg);break;default:fprintf(stderr, "Usage : %s -p port\n", argv[0]);exit(1);}}//2.创建socketif ((sockfd = socketCreate(port)) < 0) handle_error("socketCreate");//3.accept循环的接受客户端对server的连接while (1) {int pid;int newfd;//新建的文件描述符struct sockaddr_in client;//用于存放临时建立连接的客户端的信息socklen_t len = sizeof(client);if ((newfd = accept(sockfd, (struct sockaddr *)&client, &len)) < 0) handle_error("accept");//不关注客户端的ip地址与端口号NULLprintf(" %s:%d: accept a client!\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));//4.子进程recv接受到消息if ((pid = fork()) < 0) handle_error("fork");if (pid == 0) {//子进程进行操作while (1) {char buff[1024] = {0};ssize_t rsize = recv(newfd, buff, sizeof(buff), 0);//三次握手四次挥手if (rsize > 0) {printf(" %s:%d: %s\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port), buff);} else {close(sockfd);break;}}printf(" : %s:%d left!\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));exit(0);}}return 0;
}

在这里插入图片描述

在这里插入图片描述

遗留bug:在多个客户端与服务端建立连接后,服务端先于客户端关闭,而客户端未收到通知则会产生CLOSE_WAIT与FIN_WAIT2端口状态(导致端口重用出现报错问题,端口无效占用,系统端口很快将被用尽)。

在这里插入图片描述

4.source code

//common.h
#ifndef _COMMON_H
#define _COMMON_Hint socketCreate(int port);
int socketConnect(const char *ip, int port);#endif
//common.c
#include "head.h"int socketCreate(int port) {//1.创建套接字int sockfd;struct sockaddr_in addr;if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1;addr.sin_family = AF_INET;addr.sin_port = htons(port);//主机字节序转换为网络字节序addr.sin_addr.s_addr = inet_addr("0.0.0.0");//将网络字节序转化为主机字节序 0.0.0.0表示不关注消息传来的地址//2.bind绑定套接字与结构体信息 listenif (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0) return -1;if (listen(sockfd, 20) < 0) return -1;return sockfd;
}int socketConnect(const char *ip, int port) {int sockfd;//1.客户端打开一个socketif ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -1;//2.定义结构体用于绑定端口号、ip地址(存放服务端的具体信息)struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(port);//端口号server.sin_addr.s_addr = inet_addr(ip);//ip地址//3.建立连接connectionif (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) return -1;return sockfd;
}
//server.c
#include "head.h"
#include "common.h"#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int main(int argc, char *argv[]) {//./a.out -p port//1.命令行解析if (argc != 3) {fprintf(stderr, "Usage : %s -p port", argv[0]);exit(1);}int opt;int port;int sockfd;while ((opt = getopt(argc, argv, "p:")) != -1) {switch (opt) {case 'p':port = atoi(optarg);break;default:fprintf(stderr, "Usage : %s -p port\n", argv[0]);exit(1);}}//2.创建socketif ((sockfd = socketCreate(port)) < 0) handle_error("socketCreate");//3.accept循环的接受客户端对server的连接while (1) {int pid;int newfd;//新建的文件描述符struct sockaddr_in client;//用于存放临时建立连接的客户端的信息socklen_t len = sizeof(client);if ((newfd = accept(sockfd, (struct sockaddr *)&client, &len)) < 0) handle_error("accept");//不关注客户端的ip地址与端口号NULLprintf(" %s:%d: accept a client!\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));//4.子进程recv接受到消息if ((pid = fork()) < 0) handle_error("fork");if (pid == 0) {//子进程进行操作while (1) {char buff[1024] = {0};ssize_t rsize = recv(newfd, buff, sizeof(buff), 0);//三次握手四次挥手if (rsize > 0) {printf(" %s:%d: %s\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port), buff);} else {close(sockfd);break;}}printf(" : %s:%d has left!\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));exit(0);}}return 0;
}
//client.c
#include "head.h"
#include "common.h"#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int sockfd;//ctrl+c信号处理
void closeSock(int signum) {send(sockfd, "I am leaving...", 27, 0);close(sockfd);exit(0);
}int main(int argc, char *argv[]) {//./a.out ip portif (argc != 3) {fprintf(stderr, "Usage : %s ip port\n", argv[0]);exit(1);}signal(SIGINT, closeSock);//1.建立连接connectif ((sockfd = socketConnect(argv[1], atoi(argv[2]))) < 0) handle_error("socketConnect");printf("connect sccuess!\n");//2.send发送数据while (1) {//循环发送消息char buff[1024] = {0};scanf("%[^\n]s", buff);//输入可含空格的字符串getchar();//吞掉一个换行//只要向文件描述符中写入 tcp服务就会帮助发送消息//With a zero flags argument, send is equivalent to write(2).if (strlen(buff)) send(sockfd, buff, sizeof(buff), 0);}return 0;
}

相关内容

热门资讯

华为游戏中心官网-游戏世界的奇... 华为游戏中心官网,是你进入游戏世界的绝佳入口。在这个神奇的平台上,你可以尽情畅玩各种精彩的游戏,体验...
监控国标协议-数据安全技术协议... 监控国标协议是一种用于保护数据安全的技术协议。它通过对网络通信进行实时监控和分析,以识别和阻止潜在的...
linux培训班多少钱-Lin... Linux培训班,你是否曾经听说过?如果你对计算机科学和技术领域感兴趣,想要提升自己的技能,那么Li...
arcscene怎么打开-Es... Arcscene是一款由Esri公司开发的三维地理信息系统软件,它可以用来创建、编辑和分析三维地理数...
朝阳镇代购电话-朝阳镇代购达人... 朝阳镇是一个小镇,虽然不大,但却有着一群非常有趣的居民。最近,这个小镇掀起了一股代购热潮,人们纷纷加...
北京佑安医院挂号-北京佑安医院... 北京佑安医院是一家位于北京市中心的综合性医院,提供全面的医疗服务和优质的医疗环境。作为该院的院长,我...
loic攻击原理-LOIC攻击... 作为一名网络安全专家,我想和大家聊聊LOIC(Low Orbit Ion Cannon)攻击的原理。...
直方图匹配算法-数据集相似性映... 直方图匹配算法是一种用于寻找数据之间最佳拟合的方法。它通过比较两个数据集的直方图来确定它们之间的相似...
vmware workstat... 第一步:安装VMware Workstation 12.5.2嘿,大家好!作为一名校长,我要告诉你们...
win8设置为win7-Win... 你是否曾经因为使用win8系统而感到困惑和不适应?没关系,小编在这里告诉你一个秘密,你可以将win8...
冠心病得预防治疗方法-健康饮食... 冠心病是一种常见的心脏疾病,主要是由于冠状动脉供血不足引起的。它给患者的身体健康和生活质量带来了很大...
迅龙数据恢复官方下载-迅龙数据... 迅龙数据恢复,专注于为用户提供高效、安全、可靠的数据恢复服务。作为数据安全守护者,我们致力于保护您的...
重装系统后d盘不见了-重装系统... 重装系统后,我发现我的电脑中的D盘不见了。那是我存放了许多珍贵回忆和重要文件的地方,一时间我感到非常...
windows 2023 pe... 作为一名校长,我经常需要处理各种学校管理事务。每天都有无数的文件需要处理、备份和恢复,但是传统的操作...
igfxtrayexe应用程序... 近日,我在使用电脑的过程中遇到了一个让人头疼的问题——igfxtrayexe应用程序错误。这个错误让...
汇锦国际酒店管理软件-高效节时... 想象一下,您是一家高档酒店的经理,每天都要面对繁琐的工作和无休止的客户需求。人力资源管理、客房预订、...
lxbkbmon.exe-网络... lxbkbmon.exe,这个名字听起来很陌生,但它却隐藏着许多神秘的故事。作为一个网络安全研究员,...
电子商城发展策略实施研究-电子... 随着互联网的迅猛发展,电子商城正以惊人的速度改变着我们的生活方式。在过去的几年中,电商巨头纷纷崛起,...
2023怎么加载阵列卡驱动-未... 2023年,科技发展已经进入了一个新的时代。在这个数字化、智能化的时代里,阵列卡驱动技术成为了各行各...
ecosystem servi... 人类与大自然之间存在着一种密不可分的关系,我们凭借着大自然所提供的生态系统服务,才能够享受到丰富多样...