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

相关内容

热门资讯

飞鸽导航升级安卓系统,轻松提升... 亲爱的手机控们,你们有没有发现,最近你的手机导航软件有点不一样了呢?没错,就是那个我们每天都要依赖的...
安卓系统谁优化最好,安卓系统优... 你有没有发现,手机里的安卓系统就像是个大杂烩,每个品牌都给它加了点料,让它变得独一无二。但说到优化,...
安卓系统边锋兰溪麻将,畅玩手机... 你有没有发现,手机里那些小游戏,有时候比现实生活还精彩呢?比如,最近我迷上了一款叫做“边锋兰溪麻将”...
nvsip安卓系统闪退,“nv... 最近在使用NVSIP安卓系统的时候,你是不是也遇到了一个让人头疼的问题——那就是它时不时地闪退,简直...
平板安卓系统卸载软件,实用方法... 亲爱的平板用户们,你是否曾为那些占满你平板空间的安卓系统软件而烦恼呢?是不是觉得它们不仅占用内存,还...
安卓系统刷黑莓系统升级,黑莓手... 亲爱的手机控们,你们有没有想过,把你的安卓手机变成黑莓手机呢?是不是觉得这个想法有点酷炫?别急,今天...
安卓系统删除空白桌面,操作指南... 手机桌面上的空白页是不是让你觉得有点乱糟糟的?别急,今天就来教你几招,让你的安卓手机桌面焕然一新,告...
安卓关闭系统定位,隐私保护与电... 亲爱的手机控们,你们有没有想过,我们每天不离手的安卓手机,其实一直在默默记录着我们的行踪呢?没错,就...
安卓系统是全部开放,开放创新与... 你有没有想过,为什么安卓手机那么受欢迎呢?其实,这背后有一个大大的秘密——安卓系统的全部开放性!没错...
安卓11新系统耗电,安卓11系... 最近你的安卓手机是不是也成了“电老虎”?一不留神,电量就“嗖”地一下跑光了。别急,今天就来聊聊安卓1...
安卓系统定制怎么学,基于安卓系... 哇,你有没有想过,你的安卓手机其实可以是个独一无二的“艺术品”?没错,就是通过定制安卓系统,让你的手...
安卓操作做系统详解,操作原理、... 手机用久了是不是感觉有点卡?想给安卓手机来个大变身?那就得来个系统升级或者重装系统!别急,今天我就来...
安卓系统垃圾广告多,成因、影响... 亲爱的手机控们,你们有没有遇到过这种情况:手机里突然弹出一个广告,让你瞬间心情大好,然后又瞬间心情大...
安卓系统投影手机屏幕,多种方法... 哇塞,你有没有想过,你的安卓手机屏幕竟然可以变成一个大屏幕的“电影院”?没错,就是那种坐在沙发上,手...
安卓系统防走失app,安全出行... 你知道吗?现在这个世界上,科技可是越来越神奇了,连我们平时丢三落四的时候,都有办法用手机来帮忙找回来...
王者荣耀安卓系统抽奖,轻松获取... 你有没有发现,最近在王者荣耀里,安卓系统的玩家们都在疯狂讨论一个话题——抽奖!没错,就是那个让人又爱...
安卓系统硬件型号检测,全面解析... 你有没有想过,你的安卓手机里那些复杂的硬件型号,它们是怎么被识别出来的呢?今天,就让我带你一探究竟,...
手机安卓安装双系统,轻松实现一... 你有没有想过,你的安卓手机也能变身成为多面手?没错,就是那种既能玩转安卓,又能驾驭Windows 1...
巴掌手机推荐安卓系统,搭载高效... 你有没有想过,在这个手机越来越大的时代,巴掌大小的小手机竟然也能玩转安卓系统?没错,就是那种拿在手里...
安卓手机最精简系统,安卓手机最... 你有没有发现,你的安卓手机最近变得越来越慢了?是不是觉得那些复杂的设置和冗余的程序让你头疼不已?别担...