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

相关内容

热门资讯

安卓系统目前哪个最好,探索当前... 你有没有想过,手机里的安卓系统就像是一群各具特色的英雄,每个都有自己独特的技能和魅力。那么,问题来了...
安卓系统怎么把图标锁上,图标锁... 手机里的图标乱糟糟的,是不是也想给它们来个“小锁”,让它们乖乖待在原地呢?别急,今天就来教你怎么把安...
微软换账号注册安卓系统,安卓系... 你有没有想过,有一天你的安卓手机突然焕然一新,仿佛重获新生?这可不是普通的升级,而是因为微软的账号注...
怎么停止安卓系统更新,轻松关闭... 手机更新又来了!是不是每次安卓系统更新,你的手机都像被施了魔法,速度变慢,电池续航也跟着缩水?别急,...
股票买平板还是安卓系统,股票投... 最近是不是也被手机屏幕的诱惑给勾住了?想换一台新平板,但又纠结是买苹果的iPad还是安卓系统的平板呢...
iso系统比安卓安全,超越安卓... 你知道吗?在手机操作系统这个江湖里,ISO系统和安卓系统可是两大门派,各有所长。但今天,我要给你揭秘...
安卓手机系统排名图片,揭秘市场... 你有没有发现,现在安卓手机市场上,各种品牌的手机层出不穷,让人眼花缭乱?今天,就让我带你一起走进这个...
安卓系统手机没电图片,电量耗尽... 手机没电了,这可是个让人头疼的小麻烦呢!想象你正沉浸在追剧的乐趣中,突然屏幕一黑,手机没电了。这时候...
安卓系统温控模块在哪里,核心功... 你有没有遇到过手机发热的情况,是不是觉得手机就像个小暖炉,让你有点招架不住呢?别急,今天就来给你揭秘...
最流畅的安卓原生系统,探索安卓... 你有没有想过,为什么你的手机用起来有时候那么卡,有时候又那么流畅呢?这背后,其实隐藏着一个秘密——那...
安卓系统可以刷windows吗... 你有没有想过,你的安卓手机或者平板,竟然能变身成一台Windows电脑?是的,你没听错,安卓系统是可...
苹果安卓操作系统,全面对比与深... 你有没有想过,为什么你的手机里装了那么多应用,而别人的手机却看起来那么清爽?这背后,其实就是苹果的i...
安卓tv中文系统,功能解析与使... 你有没有发现,家里的安卓TV最近变得超级智能呢?没错,就是那个曾经只能看看视频、玩玩游戏的小家伙,现...
谷歌安卓10系统打不开,原因排... 最近是不是你也遇到了这个让人头疼的问题:谷歌安卓10系统打不开?别急,让我来帮你一步步排查,找出原因...
安卓系统转苹果吃鸡,体验全新i... 你知道吗?最近身边的小伙伴们都在热议一个话题:从安卓系统转到苹果手机,体验吃鸡游戏的新鲜感。这可不是...
双系统d盘安装安卓,轻松实现手... 你有没有想过,在电脑上同时拥有Windows系统和安卓系统,是不是就像拥有了两个世界的大门呢?想象一...
安卓系统后台运行程序,高效运行... 你有没有发现,手机里的安卓系统后台运行程序有时候就像那些调皮的小精灵,悄无声息地在你不知道的时候忙碌...
苹果ipad如何改安卓系统,轻... 你有没有想过,把你的苹果iPad换成安卓系统,是不是会有一种全新的使用体验呢?想象那些你熟悉的安卓应...
安卓隐藏系统状态栏,安卓隐藏系... 你有没有发现,手机屏幕下方那个小小的状态栏,有时候真的挺碍眼的?尤其是当你沉浸在游戏或者追剧的时候,...
bb10系统安装安卓,轻松实现... 你有没有想过,把那老式的BB10系统升级成安卓,让它焕发第二春呢?想象你的老手机瞬间变成了一个功能强...