c 套接字客户端程序中两个进程之间共享内存的使用导致分段错误
Usage of shared memory between 2 processes in c socket client program leads to segmentation fault
我正在创建一个具有 2 个进程的客户端程序:一个父进程 运行 定期向服务器发送数据以让它知道客户端仍处于连接状态,一个子进程接收用户输入并将其发送到服务器,如果该输入恰好是“#EXIT”,则子进程将让父进程知道程序需要通过共享内存中的变量终止,然后退出。问题是我在程序打印任何东西之前就遇到了分段错误。
st运行ge 的事情是,我第一次 运行 它给出了预期的输出并在我键入“#EXIT”时终止,但随后的所有时间我 运行 它,它立即出现段错误。一开始我以为是最后没有调用shm_unlink
,结果又加上了,结果还是一样。然后我认为这是因为我没有在子进程上调用 exit
,但我添加了它,结果还是一样。
客户代码:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
if (argc-1 != 2){
perror("Usage: ./351ChatClient [address] [port]");
return -1;
}
int sock = 0, valread;
struct sockaddr_in serv_addr;
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(atoi(argv[2]));
// Convert IPv4 and IPv6 addresses from text to binary form
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
int* shared_memory;
int shm_fd = shm_open("Transmitting", O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
ftruncate(shm_fd, sizeof(int));
shared_memory = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
shared_memory[0] = 0;
int pid = fork();
if (pid == 0){
char data[256];
while (strcmp(data, "#EXIT") != 0){
fgets(data, sizeof(data), stdin);
data[strlen(data)-1] = '[=10=]';
send(sock , data, strlen(data) , 0 );
valread = read( sock , buffer, 1024);
printf("%s\n",buffer );
}
shared_memory[0] = 1;
exit(EXIT_SUCCESS);
}
else if (pid > 0){
while(shared_memory[0] == 0){
// transmit data every couple seconds to let server know client is still connected
}
shm_unlink("Transmitting");
printf("Stopped transmitting\n");
}
return 0;
}
服务器代码:
#include <stdio.h>
#include <sys/socket.h> //For Sockets
#include <stdlib.h>
#include <netinet/in.h> //For the AF_INET (Address Family)
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#define MAXSIZE 256
void add_user(char name[], char password[]){
if (strlen(name) > MAXSIZE || strlen(password) > MAXSIZE){
// send error message to client
return;
}
FILE* fp = fopen("users.txt", "a");
char s[strlen(name)+strlen(password)+2];
strcpy(s, name);
strcat(s, "\n");
strcat(s, password);
strcat(s, "\n");
fputs(s, fp);
fclose(fp);
}
void connect_user(char name[], char password[]){
FILE* fp = fopen("users.txt", "r");
char line[MAXSIZE];
char cur_name[MAXSIZE];
bool match = false;
int line_no = 1;
while (fgets(line, sizeof(line), fp)){
line[strlen(line)-1] = '[=11=]'; // remove trailing newline
if (line_no % 2 != 0){
strcpy(cur_name, line);
if (strcmp(cur_name, name) == 0){
match = true;
}
}
else if(match){
if (strcmp(line, password) == 0){
char s[strlen(cur_name)+strlen(" is entering the queue\n")];
strcpy(s, cur_name);
strcat(s, " is entering the queue\n");
printf("%s", s);
// add user to the queue
return;
}
else{
// send invalid password message to client
return;
}
}
line_no = line_no + 1;
}
add_user(name, password); // if user not found, add it to the DB
}
int main(int argc, char *argv[]){
FILE* fp = fopen("users.txt", "a"); // creates users file if it does not exist
fclose(fp);
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr, "ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
perror("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
perror("ERROR on binding");
listen(sockfd, 5);
clilen = sizeof(cli_addr);
//Below code is modified to handle multiple clients using fork
//------------------------------------------------------------------
int pid;
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
perror("ERROR on accept");
//fork new process
pid = fork();
if (pid < 0) {
perror("ERROR in new process creation");
}
if (pid == 0) {
//child process
close(sockfd);
//do whatever you want
bzero(buffer, 256);
while(strcmp(buffer, "#EXIT") != 0){
memset(buffer, 0, sizeof(buffer)); // clear the read buffer
n = read(newsockfd, buffer, 255);
if (n < 0)
perror("ERROR reading from socket");
printf("Here is the message: %s\n", buffer);
n = write(newsockfd, "I got your message", 18);
if (n < 0)
perror("ERROR writing to socket");
}
n = write(newsockfd, "See you soon!", 18);
if (n < 0)
perror("ERROR writing to socket");
printf("Closing connection with client");
close(newsockfd);
exit(EXIT_SUCCESS);
} else {
//parent process
close(newsockfd);
}
}
//-------------------------------------------------------------------
return 0;
}
当我键入“#EXIT”时我期望收到的输出是
#EXIT
Stopped transmitting
但我收到的输出只是
segmentation fault (core dumped)
在我有机会输入任何内容之前。
我是 c 套接字和 c 的新手,所以如果我做的其他事情完全错误,请不要犹豫指出。谢谢。
编辑:我注意到的另一件事是,当客户端出现段错误时,服务器会收到大量消息。不知道为什么。
EDIT 2: 我把客户端所有的socket代码都注释掉了,重新编译,执行,还是出现segmentation fault
编辑 3:我在定义之前修复了 while 循环检查 data
,但问题仍然存在。
这是简化的客户端代码:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
if (argc-1 != 2){
perror("Usage: ./351ChatClient [address] [port]");
return -1;
}
int* shared_memory;
int shm_fd = shm_open("Transmitting", O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
ftruncate(shm_fd, sizeof(int));
shared_memory = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
shared_memory[0] = 0;
int pid = fork();
if (pid == 0){
char data[256];
while (true){
fgets(data, sizeof(data), stdin);
data[strlen(data)-1] = '[=14=]';
if(strcmp(data, "#EXIT") != 0)
break;
}
shared_memory[0] = 1;
exit(EXIT_SUCCESS);
}
else if (pid > 0){
while(shared_memory[0] == 0){
// transmit data every couple seconds to let server know client is still connected
}
shm_unlink("Transmitting");
printf("Stopped transmitting\n");
}
return 0;
}
编辑 4:我 运行 客户端使用命令 valgrind --leak-check=full --track-origins=yes ./351ChatClient 127.0.0.1 8080
通过 valgrind,这就是我得到的:
==11098== Memcheck, a memory error detector
==11098== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==11098== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==11098== Command: ./351ChatClient 127.0.0.1 8080
==11098==
==11098== Invalid write of size 4
==11098== at 0x108AA4: main (in /home/jp/Courses/csci351/projects/project1/351ChatClient)
==11098== Address 0xffffffffffffffff is not stack'd, malloc'd or (recently) free'd
==11098==
==11098==
==11098== Process terminating with default action of signal 11 (SIGSEGV)
==11098== Access not within mapped region at address 0xFFFFFFFFFFFFFFFF
==11098== at 0x108AA4: main (in /home/jp/Courses/csci351/projects/project1/351ChatClient)
==11098== If you believe this happened as a result of a stack
==11098== overflow in your program's main thread (unlikely but
==11098== possible), you can try to increase the size of the
==11098== main thread stack using the --main-stacksize= flag.
==11098== The main thread stack size used in this run was 8388608.
==11098==
==11098== HEAP SUMMARY:
==11098== in use at exit: 0 bytes in 0 blocks
==11098== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==11098==
==11098== All heap blocks were freed -- no leaks are possible
==11098==
==11098== For counts of detected and suppressed errors, rerun with: -v
==11098== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
您的 分段错误 错误可能源于 add_user(...)
和 connect_user(...)
函数中的 free(s)
,因为 char s[...]
是可变的堆栈上的大小数组,未分配给堆 free
d。你需要摆脱它们。
在服务器端和客户端,您不需要使用 wait(..)
或 waitpid(..)
等待您的子进程
此外,最好用 0
初始化数组,例如char arr[SIZE] = {0}
,也适用于可变大小的数组。
AFAI 也知道,bzero
已被弃用并降低了可移植性。所以你应该改用memset(..)
。
n = write(newsockfd, "See you soon!", 18);
在第 128 行,剩下的 5
个字符(字节)呢?垃圾文?
你最好使用 -Weverything
标志。
下面重点说一下,
shared_memory[0] = 0;
?
如果shm_open(..)
returns -1
呢?在这种情况下,您确定 shared_memory
指向您可以写入的合法位置吗?您应该 始终特别注意 return 值及其结果。
int shm_fd = shm_open("Transmitting", O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
if (shm_fd == -1) {
perror("shm_fd error");
exit(-1);
}
在我的电脑中,它在一次性 运行 之后自然地产生 shm_fd error: File exists
。
AFAI 请记住,非匿名共享映射对象会保留在计算机中,除非它被手动删除或被 unlink(..)
或 remove(..)
删除
或者,只需删除 O_EXCL
标志。
我正在创建一个具有 2 个进程的客户端程序:一个父进程 运行 定期向服务器发送数据以让它知道客户端仍处于连接状态,一个子进程接收用户输入并将其发送到服务器,如果该输入恰好是“#EXIT”,则子进程将让父进程知道程序需要通过共享内存中的变量终止,然后退出。问题是我在程序打印任何东西之前就遇到了分段错误。
st运行ge 的事情是,我第一次 运行 它给出了预期的输出并在我键入“#EXIT”时终止,但随后的所有时间我 运行 它,它立即出现段错误。一开始我以为是最后没有调用shm_unlink
,结果又加上了,结果还是一样。然后我认为这是因为我没有在子进程上调用 exit
,但我添加了它,结果还是一样。
客户代码:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
if (argc-1 != 2){
perror("Usage: ./351ChatClient [address] [port]");
return -1;
}
int sock = 0, valread;
struct sockaddr_in serv_addr;
char buffer[1024] = {0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(atoi(argv[2]));
// Convert IPv4 and IPv6 addresses from text to binary form
if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
{
printf("\nInvalid address/ Address not supported \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("\nConnection Failed \n");
return -1;
}
int* shared_memory;
int shm_fd = shm_open("Transmitting", O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
ftruncate(shm_fd, sizeof(int));
shared_memory = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
shared_memory[0] = 0;
int pid = fork();
if (pid == 0){
char data[256];
while (strcmp(data, "#EXIT") != 0){
fgets(data, sizeof(data), stdin);
data[strlen(data)-1] = '[=10=]';
send(sock , data, strlen(data) , 0 );
valread = read( sock , buffer, 1024);
printf("%s\n",buffer );
}
shared_memory[0] = 1;
exit(EXIT_SUCCESS);
}
else if (pid > 0){
while(shared_memory[0] == 0){
// transmit data every couple seconds to let server know client is still connected
}
shm_unlink("Transmitting");
printf("Stopped transmitting\n");
}
return 0;
}
服务器代码:
#include <stdio.h>
#include <sys/socket.h> //For Sockets
#include <stdlib.h>
#include <netinet/in.h> //For the AF_INET (Address Family)
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#define MAXSIZE 256
void add_user(char name[], char password[]){
if (strlen(name) > MAXSIZE || strlen(password) > MAXSIZE){
// send error message to client
return;
}
FILE* fp = fopen("users.txt", "a");
char s[strlen(name)+strlen(password)+2];
strcpy(s, name);
strcat(s, "\n");
strcat(s, password);
strcat(s, "\n");
fputs(s, fp);
fclose(fp);
}
void connect_user(char name[], char password[]){
FILE* fp = fopen("users.txt", "r");
char line[MAXSIZE];
char cur_name[MAXSIZE];
bool match = false;
int line_no = 1;
while (fgets(line, sizeof(line), fp)){
line[strlen(line)-1] = '[=11=]'; // remove trailing newline
if (line_no % 2 != 0){
strcpy(cur_name, line);
if (strcmp(cur_name, name) == 0){
match = true;
}
}
else if(match){
if (strcmp(line, password) == 0){
char s[strlen(cur_name)+strlen(" is entering the queue\n")];
strcpy(s, cur_name);
strcat(s, " is entering the queue\n");
printf("%s", s);
// add user to the queue
return;
}
else{
// send invalid password message to client
return;
}
}
line_no = line_no + 1;
}
add_user(name, password); // if user not found, add it to the DB
}
int main(int argc, char *argv[]){
FILE* fp = fopen("users.txt", "a"); // creates users file if it does not exist
fclose(fp);
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[256];
struct sockaddr_in serv_addr, cli_addr;
int n;
if (argc < 2) {
fprintf(stderr, "ERROR, no port provided\n");
exit(1);
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
perror("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
perror("ERROR on binding");
listen(sockfd, 5);
clilen = sizeof(cli_addr);
//Below code is modified to handle multiple clients using fork
//------------------------------------------------------------------
int pid;
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
perror("ERROR on accept");
//fork new process
pid = fork();
if (pid < 0) {
perror("ERROR in new process creation");
}
if (pid == 0) {
//child process
close(sockfd);
//do whatever you want
bzero(buffer, 256);
while(strcmp(buffer, "#EXIT") != 0){
memset(buffer, 0, sizeof(buffer)); // clear the read buffer
n = read(newsockfd, buffer, 255);
if (n < 0)
perror("ERROR reading from socket");
printf("Here is the message: %s\n", buffer);
n = write(newsockfd, "I got your message", 18);
if (n < 0)
perror("ERROR writing to socket");
}
n = write(newsockfd, "See you soon!", 18);
if (n < 0)
perror("ERROR writing to socket");
printf("Closing connection with client");
close(newsockfd);
exit(EXIT_SUCCESS);
} else {
//parent process
close(newsockfd);
}
}
//-------------------------------------------------------------------
return 0;
}
当我键入“#EXIT”时我期望收到的输出是
#EXIT
Stopped transmitting
但我收到的输出只是
segmentation fault (core dumped)
在我有机会输入任何内容之前。
我是 c 套接字和 c 的新手,所以如果我做的其他事情完全错误,请不要犹豫指出。谢谢。
编辑:我注意到的另一件事是,当客户端出现段错误时,服务器会收到大量消息。不知道为什么。
EDIT 2: 我把客户端所有的socket代码都注释掉了,重新编译,执行,还是出现segmentation fault
编辑 3:我在定义之前修复了 while 循环检查 data
,但问题仍然存在。
这是简化的客户端代码:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
if (argc-1 != 2){
perror("Usage: ./351ChatClient [address] [port]");
return -1;
}
int* shared_memory;
int shm_fd = shm_open("Transmitting", O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
ftruncate(shm_fd, sizeof(int));
shared_memory = (int *) mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
shared_memory[0] = 0;
int pid = fork();
if (pid == 0){
char data[256];
while (true){
fgets(data, sizeof(data), stdin);
data[strlen(data)-1] = '[=14=]';
if(strcmp(data, "#EXIT") != 0)
break;
}
shared_memory[0] = 1;
exit(EXIT_SUCCESS);
}
else if (pid > 0){
while(shared_memory[0] == 0){
// transmit data every couple seconds to let server know client is still connected
}
shm_unlink("Transmitting");
printf("Stopped transmitting\n");
}
return 0;
}
编辑 4:我 运行 客户端使用命令 valgrind --leak-check=full --track-origins=yes ./351ChatClient 127.0.0.1 8080
通过 valgrind,这就是我得到的:
==11098== Memcheck, a memory error detector
==11098== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==11098== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==11098== Command: ./351ChatClient 127.0.0.1 8080
==11098==
==11098== Invalid write of size 4
==11098== at 0x108AA4: main (in /home/jp/Courses/csci351/projects/project1/351ChatClient)
==11098== Address 0xffffffffffffffff is not stack'd, malloc'd or (recently) free'd
==11098==
==11098==
==11098== Process terminating with default action of signal 11 (SIGSEGV)
==11098== Access not within mapped region at address 0xFFFFFFFFFFFFFFFF
==11098== at 0x108AA4: main (in /home/jp/Courses/csci351/projects/project1/351ChatClient)
==11098== If you believe this happened as a result of a stack
==11098== overflow in your program's main thread (unlikely but
==11098== possible), you can try to increase the size of the
==11098== main thread stack using the --main-stacksize= flag.
==11098== The main thread stack size used in this run was 8388608.
==11098==
==11098== HEAP SUMMARY:
==11098== in use at exit: 0 bytes in 0 blocks
==11098== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==11098==
==11098== All heap blocks were freed -- no leaks are possible
==11098==
==11098== For counts of detected and suppressed errors, rerun with: -v
==11098== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
您的 分段错误 错误可能源于 add_user(...)
和 connect_user(...)
函数中的 free(s)
,因为 char s[...]
是可变的堆栈上的大小数组,未分配给堆 free
d。你需要摆脱它们。
在服务器端和客户端,您不需要使用 wait(..)
或 waitpid(..)
此外,最好用 0
初始化数组,例如char arr[SIZE] = {0}
,也适用于可变大小的数组。
AFAI 也知道,bzero
已被弃用并降低了可移植性。所以你应该改用memset(..)
。
n = write(newsockfd, "See you soon!", 18);
在第 128 行,剩下的 5
个字符(字节)呢?垃圾文?
你最好使用 -Weverything
标志。
下面重点说一下,
shared_memory[0] = 0;
?
如果shm_open(..)
returns -1
呢?在这种情况下,您确定 shared_memory
指向您可以写入的合法位置吗?您应该 始终特别注意 return 值及其结果。
int shm_fd = shm_open("Transmitting", O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
if (shm_fd == -1) {
perror("shm_fd error");
exit(-1);
}
在我的电脑中,它在一次性 运行 之后自然地产生 shm_fd error: File exists
。
AFAI 请记住,非匿名共享映射对象会保留在计算机中,除非它被手动删除或被 unlink(..)
或 remove(..)
或者,只需删除 O_EXCL
标志。