虽然没有来自客户端的连接操作,但服务器尝试接受连接并因“非套接字上的套接字操作”而失败
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虽然没有来自客户端的连接操作,但服务器尝试接受连接并因“非套接字上的套接字操作”而失败相关的知识,希望对你有一定的参考价值。
我正在使用TCP客户端 - 服务器协议做一个非常简单的应用程序。首先,我为服务器设置套接字并开始侦听客户端请求。建立连接后,客户端向我发送文件,然后我读取文件但在关闭连接后,另一个accept()调用在服务器端发生,并且失败。我只在客户端做一次connect()。我看到第二个accept上的套接字描述符值与第一个不同。
谢谢。
编辑:似乎我通过调用utility.c中的Readline()函数来覆盖套接字描述符的值。我的猜测是函数中的某些函数可能会覆盖其堆栈上方的值。
server.c
#include <sys/socket.h> /* socket definitions */
#include <sys/types.h> /* socket types */
#include <arpa/inet.h> /* inet (3) funtions */
#include <errno.h>
#include <unistd.h> /* misc. UNIX functions */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "utility.h" /* our own utility functions */
/* Global constants */
#define ECHO_PORT (2002)
#define MAX_LINE (1000)
int main(int argc, char *argv[]) {
int list_s; /* listening socket */
int conn_s; /* connection socket */
short int port; /* port number */
struct sockaddr_in servaddr; /* socket address structure */
struct sockaddr_in incoming_addr;
char* endptr; /* for strtol() */
/* Get port number from the command line, and
set to default port if no arguments were supplied */
if ( argc == 2 ) {
port = strtol(argv[1], &endptr, 0);
if ( *endptr ) {
fprintf(stderr, "ECHOSERV: Invalid port number.
");
exit(EXIT_FAILURE);
}
}
else if ( argc < 2 ) {
port = ECHO_PORT;
}
else {
fprintf(stderr, "ECHOSERV: Invalid arguments.
");
exit(EXIT_FAILURE);
}
/* Create the listening socket */
list_s = socket(AF_INET, SOCK_STREAM, 0);
if (list_s < 0) {
fprintf(stderr, "ECHOSERV: Error creating listening socket.
");
exit(EXIT_FAILURE);
}
else {
fprintf(stdout, "Socket successfully created.
");
}
/* Set all bytes in socket address structure to
zero, and fill in the relevant data members */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
/* Bind the socket descriptor "list_s" to the servaddr which defines the port number. */
if ( bind(list_s, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling bind()
");
exit(EXIT_FAILURE);
}
else {
fprintf(stdout, "Socket successfully binded to port no %d.
", port);
}
if ( listen(list_s, 1) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling listen()
");
exit(EXIT_FAILURE);
}
else {
fprintf(stdout, "Now listening to port: %d.
", port);
}
/* Enter an infinite loop to respond
to client requests and echo input */
while (1) {
/* Wait for a connection, then accept() it */
socklen_t incoming_addr_len = sizeof(incoming_addr);
int server_socket = list_s;
printf("The value of list_s is: %d.
", list_s);
conn_s = accept(server_socket, (struct sockaddr*)(&incoming_addr), &incoming_addr_len);
if ( conn_s < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling accept(): %s.
", strerror(errno));
exit(EXIT_FAILURE);
}
/* Read the data from the socket descriptor which in this case */
/* is the size of the file that is to be received. */
int data_size;
Readline(conn_s, &data_size, sizeof(data_size));
/* Read data from the given socket descriptor conn_s and then
* write the same data to the descriptor again to be sent as
* a response. */
char buffer[data_size];
Readline(conn_s, buffer, data_size);
printFile(buffer, data_size);
/* Close the connected socket */
if ( close(conn_s) < 0 ) {
fprintf(stderr, "ECHOSERV: Error calling close()
");
exit(EXIT_FAILURE);
}
}
}
client.c
#include <sys/socket.h> /* socket definitions */
#include <sys/types.h> /* socket types */
#include <arpa/inet.h> /* inet (3) funtions */
#include <unistd.h> /* misc. UNIX functions */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "utility.h" /* Our own utility functions */
/* Global constants */
#define MAX_LINE (1000)
#define DATA_IN_FILE (4)
static const char* const kFileName = "/home/pb/Desktop/workspace/Networking/practice_project/practice_project_test_file_1";
/* Function declarations */
int ParseCmdLine(int argc, char *argv[], char **szAddress, char **szPort);
/* main() */
int main(int argc, char *argv[]) {
int conn_s; /* connection socket */
short int port; /* port number */
struct sockaddr_in servaddr; /* socket address structure */
char buffer[MAX_LINE]; /* character buffer */
char *szAddress; /* Holds remote IP address */
char *szPort; /* Holds remote port */
char *endptr; /* for strtol() */
/* Get command line arguments */
ParseCmdLine(argc, argv, &szAddress, &szPort);
/* Set the remote port */
port = strtol(szPort, &endptr, 0);
if ( *endptr ) {
printf("ECHOCLNT: Invalid port supplied.
");
exit(EXIT_FAILURE);
}
/* Create the listening socket */
if ( (conn_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
fprintf(stderr, "ECHOCLNT: Error creating listening socket.
");
exit(EXIT_FAILURE);
}
/* Set all bytes in socket address structure to
zero, and fill in the relevant data members */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
/* Set the remote IP address */
if ( inet_aton(szAddress, &servaddr.sin_addr) <= 0 ) {
printf("ECHOCLNT: Invalid remote IP address.
");
exit(EXIT_FAILURE);
}
/* connect() to the remote echo server */
if ( connect(conn_s, (struct sockaddr *) &servaddr, sizeof(servaddr) ) < 0 ) {
printf("ECHOCLNT: Error calling connect()
");
exit(EXIT_FAILURE);
}
FILE* f = fopen(kFileName, "rb");
if (f == NULL) {
perror("Error ");
return -1;
}
fseek(f, 0, SEEK_END);
int size_of_file = ftell(f);
rewind(f);
char file_buffer[size_of_file];
int items_read = fread(file_buffer, 1, size_of_file, f);
if (items_read != size_of_file) {
printf("Read the rest.
");
}
/* First sending to the server the size of the data in the */
/* file in bytes as an integer. */
sendData(conn_s, &size_of_file, sizeof(int));
/* This is the actual file now that we have let the server */
/* know the size of the file to be send. */
sendData(conn_s, file_buffer, size_of_file);
return EXIT_SUCCESS;
}
int ParseCmdLine(int argc, char *argv[], char **szAddress, char **szPort) {
int n = 1;
while ( n < argc ) {
if ( !strncmp(argv[n], "-a", 2) || !strncmp(argv[n], "-A", 2) ) {
*szAddress = argv[++n];
}
else if ( !strncmp(argv[n], "-p", 2) || !strncmp(argv[n], "-P", 2) ) {
*szPort = argv[++n];
}
else if ( !strncmp(argv[n], "-h", 2) || !strncmp(argv[n], "-H", 2) ) {
printf("Usage:
");
printf(" timeclnt -a (remote IP) -p (remote port)
");
exit(EXIT_SUCCESS);
}
++n;
}
return 0;
}
utility.c
#include "utility.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
int printFile(char *binary_buffer, int buffer_size) {
int current_position = 0;
while (current_position < buffer_size) {
if(binary_buffer[current_position] == 0) {
printf("
");
printf("Type 1.
");
current_position = read_first_type(binary_buffer, current_position);
}
else if(binary_buffer[current_position] == 1) {
printf("
");
printf("Type 2.
");
current_position = read_second_type(binary_buffer, current_position);
}
}
return 0;
}
int read_first_type(const char* binary, const int offset) {
int size_of_units = 2;
int no_of_units;
short unit_buffer[1];
// current position is always Type and no_of_units is amount which is a byte off of Type.
no_of_units = binary[offset+1];
printf("No of units is %d.
", no_of_units);
for (int i=0; i<no_of_units; ++i) {
// This is a Big-endian system so the values will bit off.
memcpy(unit_buffer, (binary+offset+2)+i*size_of_units, 2);
printf("Type 1 unit has number: %d.
", unit_buffer[0]);
}
return (offset + no_of_units*size_of_units + 2);
}
int read_second_type(const char* binary, const int offset) {
int no_of_units;
int current_position = 4;
char amount[4];
memcpy(amount, binary+offset+1, 3);
amount[3] = ' ';
no_of_units = atoi(amount);
printf("No of units is %d.
", no_of_units);
char unit_buffer[5];
int count;
for(int i=0; i<no_of_units; ++i) {
count = 0;
while(1) {
memcpy(unit_buffer+count, binary+offset+current_position+count, 1);
if (unit_buffer[count] == 44) {
unit_buffer[count] = ' ';
current_position += count+1;
break;
}
if (unit_buffer[count] == 0 || unit_buffer[count] == 1) {
unit_buffer[count] = ' ';
printf("Type 2 unit has number: %d.
",atoi(unit_buffer));
return (offset+current_position+count);
}
count++;
}
printf("Type 2 unit has number: %d.
",atoi(unit_buffer));
}
return offset+current_position+count;
}
/* Read a line from a socket */
ssize_t Readline(int sockd, void *vptr, size_t maxlen) {
ssize_t n, rc;
char c, *buffer;
buffer = vptr;
for ( n = 0; n < maxlen; n++ ) {
if ( (rc = read(sockd, &c, 1)) == 1 ) {
*buffer++ = c;
if ( c == '
' )
break;
}
else if ( rc == 0 ) {
if ( n == 0 )
return 0;
else
break;
}
else {
if ( errno == EINTR )
continue;
return -1;
}
}
*buffer = 0;
return n;
}
/* Write a line to a socket */
ssize_t sendData(int sockd, const void *vptr, size_t n) {
size_t nleft;
ssize_t nwritten;
const char *buffer;
buffer = vptr;
nleft = n;
while ( nleft > 0 ) {
if ( (nwritten = write(sockd, buffer, nleft)) <= 0 ) {
if ( errno == EINTR )
nwritten = 0;
else
return -1;
}
nleft -= nwritten;
buffer += nwritten;
}
return n;
}
答案
如果在ReadLine
中读取了完整的缓冲区(即maxlen
在循环中到达),那么在最后一个字符读取后仍然会写入0x0,从而导致缓冲区超出:
ssize_t Readline(int sockd, void *vptr, size_t maxlen) {
...
for ( n = 0; n < maxlen; n++ ) {
if ( (rc = read(sockd, &c, 1)) == 1 ) {
*buffer++ = c;
...
*buffer = 0; <<<<<<<<<<<< might be vptr[maxlen+1], i.e. buffer overflow
return n;
这样做几乎总会发生
Readline(conn_s, &data_size, sizeof(data_size));
只有当组成data_size
的八位字节在缓冲区结束之前包含
时才会发生,但是那时你会读错了数字。
此缓冲区溢出可能会导致相邻内存损坏,这可能会导致您看到的问题。
以上是关于虽然没有来自客户端的连接操作,但服务器尝试接受连接并因“非套接字上的套接字操作”而失败的主要内容,如果未能解决你的问题,请参考以下文章