【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;
}

相关内容

热门资讯

安装新风系统能防雾霾吗,安装新... 安装新风系统能有效防止雾霾吗?随着环境污染问题的日益严重,雾霾已经成为影响人们健康和生活质量的重要因...
安装烟台新风系统,打造健康呼吸... 烟台新风系统安装指南:打造健康呼吸环境随着城市化进程的加快,空气质量问题日益凸显。为了改善室内空气质...
安装系统总卡在镜像界面,原因及... 系统安装卡在镜像界面:原因及解决方案在电脑系统安装过程中,有时会遇到卡在镜像界面的问题,这不仅影响了...
清洗系统安装,关键步骤与注意事... 清洗系统安装:关键步骤与注意事项随着工业生产和生活水平的不断提高,清洗系统的应用越来越广泛。清洗系统...
安卓系统安装语言,轻松上手,享... 安卓系统安装指南:轻松上手,享受智能生活一、准备工作在开始安装安卓系统之前,您需要做好以下准备工作:...
安装消防安全系统价格表,全面解... 安装消防安全系统价格表:全面解析各类消防设备成本随着社会的发展和科技的进步,消防安全问题日益受到重视...
安装新系统电脑怎么重启,安装新... 安装新系统后电脑重启的详细指南在电脑安装新系统后,重启是完成安装过程的关键步骤。本文将详细讲解安装新...
白山实验室通风系统安装,保障实... 白山实验室通风系统安装:保障实验室安全与环保的关键步骤随着科学技术的不断发展,实验室在科研、教学等领...
安装小米10系统更新,簹鏃呮父... 小米10系统更新指南:轻松升级至最新版本一、检查系统版本在开始更新之前,首先需要确认您的手机是否已经...
安卓系统安装app很慢,安卓系... 安卓系统安装App速度慢的原因及解决方法随着智能手机的普及,安卓系统因其开放性和丰富的应用生态而受到...
安装银河麒麟系统的方式,银河麒... 银河麒麟系统安装指南随着我国科技实力的不断提升,国产操作系统逐渐成为市场的主流。银河麒麟系统作为我国...
安装消防防排烟系统,消防防排烟... 消防防排烟系统的安装规范及重要性随着城市化进程的加快,高层建筑和大型公共场所越来越多,消防安全问题日...
昂达610主板安装系统,丕噩賲... 昂达610主板安装系统全攻略一、准备工作在开始安装系统之前,我们需要做好以下准备工作: 准备安装盘...
安卓系统手机安装北斗,安卓系统... 安卓系统手机安装北斗导航的详细步骤一、了解北斗导航系统北斗卫星导航系统是中国自主研发的全球卫星导航系...
安装系统自带office200... 系统自带Office 2007安装指南随着电脑的普及,Microsoft Office已经成为许多用...
白色系统门窗安装方法,白色系统... 白色系统门窗安装方法详解随着现代家居装修的不断发展,白色系统门窗因其简约大方、耐候性强、保温隔热等优...
白云区新风系统安装,打造健康呼... 白云区新风系统安装:打造健康呼吸环境,提升生活品质随着城市化进程的加快,空气质量问题日益受到人们的关...
安装云服务系统,云服务器系统安... 云服务器系统安装指南随着云计算技术的不断发展,云服务器已经成为企业和个人用户的重要选择。本文将为您详...
宝元系统安装备份,宝元系统安装... 宝元系统安装与备份指南随着数控技术的不断发展,宝元(LNC)数控系统在机床行业中得到了广泛应用。为了...
安装装双系统安装教程, 引言 安装双系统安装教程 引言随着计算机技术的不断发展,许多用户为了满足不同的工作或娱乐需求,选择在电脑上...