郑州大学操作系统实验 套接字通信实验中出现 段错误:核心已转储问题

一.修改后的代码以及程序运行结果

错误分析放在第二部分,只需要修改后的程序代码只看第一部分即可。

1、创建并打开服务器程序与客户端程序,观察结果

源程序:

创建tcpthreadserver.c

#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h> 
#include <semaphore.h>
#include <arpa/inet.h>
#define LinkNum 5   //连接数
int client_sockfd[LinkNum];        /*分别记录服务器端的套接字 与连接的多个客户端的套接字*/
int server_sockfd = -1;//命名套接字
int curLink=0; //当前连接数
sem_t mutex; //表示连接数的资源信号量 
char stopmsg[100]; //服务器端发送消息缓冲区
pthread_t thread;
void quit()
{//客户服务通信结束处理函数
int i;
char*msg="服务器将要关闭了!";
while(1)
  {
  if(strcmp(stopmsg,"quit")==0)
    {//如果服务器端发送消息为"quit",则提示服务器将关闭 
     printf("服务器关闭!\n");
     for(i=0;i<LinkNum;i++)
     if(client_sockfd[i]!=-1)
     write(client_sockfd[i], msg, sizeof(msg)); /*依次向继续保持连接的客户端发出“服务器将关闭”的通知消息*/
     close(server_sockfd);  //关闭服务器监听套接字 
     sem_destroy(&mutex); //销毁连接数资源信号量mutex
     exit(0);
    }
  }
}
void rcv_snd(int n)
{//服务器与客户端的收发通信函数,n为连接数组序号
int i=0;
int retval;
char recv_buf[1024];    //接收消息缓冲区
char send_buf[1024];    //发送消息缓冲区
int client_len = 0;
int rcv_num;            //从客户端接收到的消息长度 
pthread_t tid;          //线程id
tid = pthread_self();   //获取当前线程id
printf("-----------服务器线程id=%lu使用套接字%d,n=%d与客户机对 话开始...\n",tid,client_sockfd[n],n);
do
{//服务器与客户端循环发送接收消息
     memset(recv_buf, 0, 1024);//接收消息缓冲区清零 
     printf("服务器线程id=%lu,套接字%d,n=%d等待客户端回应...\n",tid,client_sockfd[n],n); 
     rcv_num = read(client_sockfd[n], recv_buf,sizeof(recv_buf));
     printf("服务器线程id=%lu,套接字%d,n=%d从客户端接受的消息长度=%zu\n",tid,client_sockfd[n],n,strlen(recv_buf));
    printf("3.服务器线程id=%lu,套接字%d,n=%d<---客户端,服务器从客户端接受的消息是:(%d) :%s\n",tid,client_sockfd[n],n,rcv_num,      recv_buf); 
     if(rcv_num==0)  break;
     sleep(1);
     if(strncmp(recv_buf,"!q",2)==0)  break;   //若 接收到"!q",则结束循环,通信结束
     printf("4.服务器线程id=%lu,套接字%d,n=%d--->客户端,请输入服务器要发送给客户机的消息:",tid,client_sockfd[n],n);
     memset(send_buf, 0, 1024);  //发送消息缓冲区清零 
     scanf("%s",send_buf);             //服务器端键盘输入字符串消息,输入"!q"或"quit",则通信结束
     strcpy(stopmsg,send_buf);
     write(client_sockfd[n], send_buf, sizeof(send_buf)); 
     if(strncmp(send_buf,"!q",2)==0) break;  //若服务器端发送"!q",则结束循环,通信结束 
     if(strncmp(send_buf,"quit",4)==0) break;  //若服务器端发送"quit",则结束循环,通信结束 
}while(strncmp(recv_buf,"!q",2)!=0);
printf("-----------服务器线程id=%lu,套接字%d,n=%d与客户机对话结束---------\n",tid,client_sockfd[n],n);
close(client_sockfd[n]);       //关闭连接套接字
client_sockfd[n]=-1;           //被关闭连接套接字数组项置 为空闲
curLink--;//当前连接数减1
printf("当前连接数为:%d(<=%d)\n",curLink,LinkNum);//输出当前连接数和最大连接数
sem_post(&mutex);//释放可用连接数资源信号量mutex
pthread_exit(&retval); //当前服务器线程结束
}
int main(void)
{
char recv_buf[1024];//接收消息缓冲区
char send_buf[1024]; //发送消息缓冲区
int client_len = 0;
struct sockaddr_in server_addr;//服务器端协议地址
struct sockaddr_in client_addr; //客户端协议地址
int i=0;//连接套接字数组循环变量
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;  //指定网络套接字 
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //接受所有IP 地址的连接
server_addr.sin_port = htons(9736);       //绑定到9736端口 
bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));//协议套接字命名为server_sockfd
printf("1、服务器开始listen...\n");
listen(server_sockfd, LinkNum); /*创建连接数最大为LinkNum的 套接字队列,监听命名套接字,listen不会阻塞,它向内核报告套接字和最大连接数*/
signal(SIGCHLD, SIG_IGN);   //忽略子进程停止或退出信号 
printf("输入!q,服务结束.\n");//输入!q,服务结束
pthread_create(&thread,NULL,(void*)(&quit),NULL);//创建线程, 执行函数quit
for(i=0;i<LinkNum;i++)  client_sockfd[i]=-1;//初始化连接队列 
sem_init(&mutex,0,LinkNum); //信号量mutex初始化为连接数 
while(1)
{
for(i=0;i<LinkNum;i++) //搜寻空闲连接
if(client_sockfd[i]==-1) break;
if(i==LinkNum)
{//如果达到最大连接数,则客户等待
printf("已经达到最大连接数%d,请等待其它客户释放连接...\n",LinkNum);
sem_wait(&mutex);           //阻塞等待空闲连接
continue;                   //被唤醒后继续监测是否有空闲连接 
}
client_len = sizeof(client_addr);
printf("2、服务器开始accept...i=%d\n",i); 
client_sockfd[i] = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len);
curLink++;//当前连接数增1
sem_wait(&mutex); //可用连接数信号量mutex减1 
printf("当前连接数为:%d(<=%d)\n",curLink,LinkNum);
printf("连接来自:连接套接字号=%d,IP地址=%s,端口号=%d\n",client_sockfd[i],inet_ntoa(client_addr.sin_addr),ntohs( client_addr.sin_port));//输出客户端地址信息 
pthread_create(malloc(sizeof(pthread_t)),NULL,(void*)(&rcv_snd ),(void*)i);
}
}

创建tcpclient.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
int main(void)
{
int sockfd;                        //客户端套接字描述符 
int len = 0;
struct sockaddr_in address; //套接字协议地址 
char snd_buf[1024];  //发送消息缓冲区 
char rcv_buf[1024];  //接收消息缓冲区 
int result;
int rcv_num;//接收消息长度
pid_t cpid;//客户进程标识符
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("客户端创建套接字失败!\n");
return 1;
}
address.sin_family = AF_INET;      //使用网络套接字 
address.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器地址 
address.sin_port = htons(9736);    //服务器所监听的端口 
if(inet_aton("127.0.0.1",&address.sin_addr)<0)
{
printf("inet_aton error.\n");
return -1;
}
len = sizeof(address);
cpid=getpid();              //获取客户进程标识符 
printf("1、客户机%d开始connect服务器...\n",cpid); 
result = connect(sockfd, (struct sockaddr*)&address, len);
if(result == -1)
{
perror("客户机connect服务器失败!\n");
exit(1);
}
printf("-----------客户机%d与服务器线程对话开始...\n",cpid); 
do
{//客户机与服务器循环发送接收消息
printf("2.客户机%d--->服务器:sockfd=%d,请输入客户机要发送给服务器的消息:",cpid,sockfd);
memset(snd_buf, 0, 1024);          //发送缓冲区清零
scanf("%s",snd_buf); //键盘输入欲发送给服务器的消息字符串 
write(sockfd, snd_buf, sizeof(snd_buf));  //将消息发送到套接字 
if(strncmp(snd_buf,"!q",2)==0) break;//若发送"!q",则结束循环, 通信结束
memset(rcv_buf, 0, 1024);          //接收缓冲区清零 
printf("客户机%d,sockfd=%d 等待服务器回应...\n",cpid,sockfd); 
rcv_num = read(sockfd, rcv_buf, sizeof(rcv_buf));
printf("客户机%d,sockfd=%d 从服务器接收的消息长度=%ld\n",cpid,sockfd,strlen(rcv_buf));
printf("3.客户机%d<---服务器:sockfd=%d,客户机从服务器接收到的消息是: (%d) :%s\n", cpid,sockfd,rcv_num, rcv_buf); //输出客户机从服务器接收的消息
sleep(1);
if(strncmp(rcv_buf,"quit",4)==0) break;   //如果收到"quit", 则结束循环,通信结束
}while(strncmp(rcv_buf,"!q",2)!=0); //如果收到"!q",则结束循环, 通信结束
printf("-----------客户机%d,sockfd=%d 与服务器线程对话结束--- ------\n",cpid,sockfd);
close(sockfd);       //关闭客户机套接字
}

编译链接命令:

gcc tcpthreadserver.c -o tcpthreadserver -lpthread
gcc tcpclient.c -o tcpclient

运行命令:

./tcpthreadserver
./tcpclient

交互与结果:

多客户端运行结果

二.问题分析

代码输入后,编译出现问题,报错显示thread未定义,翻看讲义后也未找到,故自己增加了

pthread_t thread;

然后可以成功编译

分别打开客户端与服务端,建立连接时报错,显示段错误(核心已转储)

在网上查询后,尝试用gdb分析错误

一般核心数据大小为0,不能直接使用ulimit -c unlimited指令修改,该指令只在当前终端关闭后就会失效,需要改/etc/security/limits.conf的数据,在底部加入soft core unlimitedhard core unlimited

同时使用sudo systemctl disable apport.service或者 sudo service apport stop ,关闭错误报告功能,重启虚拟机。

此时输入cat /proc/sys/kernel/core_pattern 可以查询到核心文件

此时再次启动两个终端,先后启动服务端与客户端,再次报错后,会出现一个core

此时利用gdb分析,格式是gdb 程序路径 核心路径

同时输入bt回溯定位,便于找到相关报告

根据错误信息,它指示程序在执行时遇到了一个名为 __strlen_evex 的函数,该函数定义在 strlen-evex.S 文件中的第 77 行。然而,系统无法找到该文件或目录。

根据搜索的结果,只能找到这个目录是包含了与 x86_64 架构相关的多架构支持文件

尝试在电脑里搜索该文件夹,sudo find / -type d -name multiarch

即使处于管理员权限状态,还是显示权限不够,这条路走死了。

换个思路,从创建可执行文件的报错着手。必须解决的错误解决了,从各个警报入手。

这个警告是由于格式字符串中的格式指示符 %u 期望的是 unsigned int 类型的参数,但实际上传递给 printf 函数的第二个参数 tid pthread_t 类型(在此情况下是 long unsigned int)。这种类型不匹配导致了警告。故将%u改为%lu

警告5052535765也是一样的错误,同样的方法修改,不再赘述。

这时可以选择编译并运行

仍然报错

这个警告是由于格式字符串中的格式指示符 %d 期望的是 int 类型的参数,但实际上传递给 printf 函数的第五个参数 strlen(recv_buf) size_t 类型(在此情况下是 long unsigned int)。这种类型不匹配导致了警告。

在先前错误显示与strlen函数的调用有关,大概率是这个函数出问题

size_t 类型的参数,改用 %zu 格式指示符

再次报错。

这个警告是因为在代码中使用了未声明的函数 inet_ntoa。编译器发出警告,因为它无法确定该函数的定义和返回类型。

inet_ntoa 函数用于将网络字节序的 IP 地址转换为点分十进制表示的字符串形式。要解决这个警告,需要包含 <arpa/inet.h> 头文件,该头文件声明了 inet_ntoa 函数。

首部添加 #include <arpa/inet.h>

连接正常

此时还有最后一个警告

这个警告是因为在代码中进行了从整数类型到指针类型的强制转换。编译器发出警告,因为整数类型和指针类型的大小可能不同,可能导致错误的内存访问,应该使用正确的类型进行转换,以确保类型匹配。

pthread_create(malloc(sizeof(pthread_t)),NULL,(void*)(&rcv_snd ),(void*)i);

应将原代码改为

pthread_t *thread = malloc(sizeof(pthread_t));

pthread_create(thread, NULL, rcv_snd, (void*)i);

但是更改后产生了新警告

这个警告是由于在调用 pthread_create 函数时,传递给第三个参数 rcv_snd 的函数指针类型与 pthread_create 期望的类型不匹配。

根据错误消息,rcv_snd 函数的类型为 void (*)(int),但是 pthread_create 函数期望的是 void* (*)(void*) 类型的函数指针。再修改过于麻烦,保留原代码。(事实证明,能运行就别改,越改越抽象)

同样道理,修改客户端的警告。

最终生成效果

最终运行效果

同时连接两个客户端也正常运行

理论上的根本问题是,使用了 inet_ntoa 函数,但是没有添加 <arpa/inet.h> 头文件

需要在首部添加#include <arpa/inet.h>

  • 28
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值