我正在开发一个TCP客户端服务器程序,它应该支持多个使用线程的客户端。
套接字创建,连接,绑定和接受按预期工作,因为我在运行代码时没有收到任何错误。 但是每当我尝试从服务器读取()时代码进入无限循环并且没有任何反应。
我首先尝试从服务器写入,并将写入结果写入服务器的终端。
客户代码:
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
int main(int argc, char *argv[]) {
//Get command line arguments
unsigned int port = atoi(argv[2]);
int length = atoi(argv[3]); //Number of bytes to read
char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in serv_addr;
int sockfd = -1;
//int rv;
//char ip[100];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0) {
perror("getaddrinfo error\n");
exit(1);
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0)
continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0)
break;
close(sockfd);
sockfd = -1;
}
// inet_aton(ip, &h.sin_addr);
freeaddrinfo(servinfo);
//Open file for reading
FILE *fp;
fp = fopen(FILE_ADDR, "r");
if (fp == NULL) {
perror("Error in file open\n");
}
printf("file opened\n");
size_t numOfbytesRead = fread(buffer, sizeof(char), length, fp);
if (numOfbytesRead != length) {
perror("Error reading from file\n");
}
printf("Buffer is %s\n", buffer);
char* ptr;
unsigned int N = strtoul(argv[3],&ptr,10);
int convertedNum = htonl(N);
if (write(sockfd, &convertedNum, sizeof(unsigned int)) < 0) { //Send number of bytes
perror("error writing to socket");
}
if (write(sockfd, buffer, sizeof(buffer) < 0)) {//Send bytes read from file
perror("error writing to socket");
}
printf("send is done \n");
int bytes_read = read(sockfd, recvBuf, sizeof(recvBuf)); //Recieve response from server
if (bytes_read <= 0) {
perror("Error in recieving result from server\n");
}
unsigned int C = 0;
sprintf(recvBuf[0], "%d", C);
fclose(fp);
printf("# of printable characters: %u\n", C);
exit(0);
free(buffer);
free(recvBuf);
}
服务器代码:
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
static volatile int keepRunning = 1;
int pcc_total[159];
void intHandler(int dummy) {
keepRunning = 0;
}
void *compute(void *socket_desc) {
int count = 0;
int sock = *(int*) socket_desc;
printf("now will allocate N \n");
int n=0;
if (write(sock, "hi", 2) < 0) { //Send number of bytes
perror("error writing to socket\n");
}
if (read(sock, &n, sizeof(unsigned int)) < 0) {
perror("Error reading from socket\n");
exit(1);
}
int N = ntohl(n);
printf("len is %d\n", N);
char* data = calloc(N, sizeof(char));
int len = read(sock, data, N);
printf("data is %s\n", data);
if (len < 0) {
perror("Error reading from socket\n");
exit(1);
}
for (int i = 0; i < len; i++) {
int tmp = 0;
sprintf(data[i], "%d", tmp);
if (tmp >= 32 & tmp <= 126) {
count++;
__sync_fetch_and_add(&pcc_total[tmp], 1);
}
}
char scount[100];
atoi(count);
write(sock, count, strlen(scount));
free(data);
pthread_exit(NULL);
close(sock);
exit(0);
}
int main(int argc, char *argv[]) {
unsigned int port = atoi(argv[1]);
signal(SIGINT, intHandler);
int socket_desc, client_sock, c, *new_sock;
struct sockaddr_in server, client;
c = sizeof(struct sockaddr_in);
socket_desc = socket( AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
perror("Could not create socket");
exit(1);
}
printf("socket created\n");
memset(&server, 0, c);
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(port);
if (0 != bind(socket_desc, (struct sockaddr*) &server, sizeof(server))) {
perror("\n Error : Bind Failed \n");
exit(1);
}
printf("bind created\n");
if (0 != listen(socket_desc, 10)) {
perror("\n Error : Listen Failed \n");
exit(1);
}
printf("listen created\n");
while (keepRunning) {
client_sock = accept(socket_desc, (struct sockaddr *) &client,
(socklen_t*) &c);
if (client_sock < 0) {
perror("\n Error : Accept Failed\n");
exit(1);
}
printf("accept created\n");
pthread_t tid;
new_sock = malloc(100*sizeof(int));
*new_sock = client_sock;
if ((pthread_create(&tid, NULL, compute, (void*) new_sock)) < 0) {
perror("could not create thread\n");
exit(1);
}
printf("thread created\n");
// close socket
close(client_sock);
free(new_sock);
pthread_join(tid, NULL);
}
exit(0);
}
我使用以下命令运行代码:
gcc -std = c99 -O3 -Wall -o pcc_server pcc_server.c -pthread
gcc -std = gnu99 -O3 -Wall -o pcc_client pcc_client.c
答案 0 :(得分:3)
您的代码存在许多问题。
在客户端:
致电fread()
时,您需要使用"rb"
代替"r"
。
在调用printf()
输出实际读取的文件数据时,您不会终止buffer
,或将其length
传递给printf()
。你需要这样做。
您要将htonl()
的返回值分配给int
而不是unsigned int
。
在致电write()
发送buffer
时,当您应该使用sizeof(buffer)
或length
时,您正在使用N
(为什么你使用两个单独的变量来保存相同的命令行参数值?)。 buffer
是指向使用malloc()
分配的内存的指针,因此sizeof(buffer)
与sizeof(void*)
相同,这不是您想要的。此外,您甚至没有正确地调用write()
,因为您的括号完全错误(在发送write()
时,它们在之前的convertedNum
来电时是正确的。)
同样,在致电read()
阅读recvBuf
时,当您应该使用sizeof(recvBuf)
时,您正在使用10
,sicne recvBuf
也是指向malloc
内存的指针。
您没有读取服务器在连接时发送给客户端的"hi"
问候语,因此您将这些字节与下一条消息的以下大小值的字节混为一谈,从而最终损坏的C
值。
在服务器端:
您的compute
线程例程向客户端发送"hi"
问候语,但它不使用任何类型的分隔符,例如为问候语添加长度前缀,或者使用一行来终止问候语中断或空字符或其他唯一字符,以将其与任何后续数据分开。您应该始终以某种方式划分邮件。
您正在关闭accept
ed套接字,并在创建工作线程以处理该客户端后立即释放malloc
ed new_sock
。你正在从线索的谚语背后扯下记忆。线程需要是关闭套接字并在使用它们时释放内存的线程,而不是accept
循环。
线程确实尝试关闭套接字(但没有释放内存),但在之后首先调用pthread_exit()
,这是错误的。 pthread_exit()
终止调用线程,因此它需要是线程调用的 last 事物(不要调用exit()
!)。实际上,甚至不要直接拨打pthread_exit()
,只需return
compute()
,{p = <}},pthreads库会为你调用pthread_exit()
,然后将其传递给void*
{您选择{1}}的{1}}值。
您的return
循环根本不应该调用accept
。它会阻塞调用线程,直到指定的线程终止。这违背了使用线程处理客户端的整个目的,并阻止您的服务器一次接受多个客户端。如果您要使用pthread_join()
,它应该在pthread_join()
循环结束后,因此您可以在退出应用程序之前等待可能仍在运行的任何工作线程。但这也意味着要跟踪accept
返回的pthread_t
值,这是更多的工作。
话虽如此,请尝试使用此代码:
客户代码:
pthread_create()
服务器代码:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
char* readMsg(int sockfd, size_t *msgSize)
{
*msgSize = 0;
unsigned int length = 0;
int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
return NULL;
}
length = ntohl(length);
char *buffer = malloc(length+1);
if (!buffer) {
perror("Error in allocating memory to receive message from server\n");
return NULL;
}
char *pbuf = buffer;
unsigned int buflen = length;
while (buflen > 0) {
bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
if (bytes_read <= 0) {
perror("Error in receiving message from server\n");
free(buffer);
return NULL;
}
pbuf += bytes_read;
buflen -= bytes_read;
}
*msgSize = length;
return buffer;
}
int sendMsg(int sockfd, char *msg, size_t msgSize)
{
unsigned int convertedNum = htonl(msgSize);
if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
perror("error writing to socket");
return -1;
}
if (write(sockfd, msg, msgSize) < 0) { //Send bytes
perror("error writing to socket");
return -1;
}
return 0;
}
int main(int argc, char *argv[]) {
char* ptr;
//Get command line arguments
unsigned int port = atoi(argv[2]);
unsigned int length = strtoul(argv[3], &ptr, 10); //Number of bytes to read
char* buffer = malloc(length); //Buffer to hold data read from file
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in serv_addr;
int sockfd = -1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0) {
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0)
continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0)
break;
close(sockfd);
sockfd = -1;
}
freeaddrinfo(servinfo);
if (sockfd == -1) {
perror("socket create/connect error\n");
return 1;
}
size_t msgSize;
char *msg = readMsg(sockfd, &msgSize);
if (!msg) {
close(sockfd);
return 1;
}
printf("%.*s\n", (int)msgSize, msg);
free(msg);
//Open file for reading
FILE *fp = fopen(FILE_ADDR, "rb");
if (fp == NULL) {
perror("Error in file open\n");
close(sockfd);
return 1;
}
printf("file opened\n");
if (fread(buffer, 1, length, fp) != length) {
perror("Error reading from file\n");
fclose(fp);
close(sockfd);
return 1;
}
fclose(fp);
printf("Buffer is %.*s\n", (int)length, buffer);
if (sendMsg(sockfd, buffer, length) != 0) {
free(buffer);
close(sockfd);
return 1;
}
free(buffer);
printf("send is done \n");
msg = readMsg(sockfd, &msgSize); // response from server
if (!msg) {
close(sockfd);
return 1;
}
printf("# of printable characters: %.*s\n", (int)msgSize, msg);
free(msg);
return 0;
}
答案 1 :(得分:1)
我认为你应该删除这两行
close(client_sock);
free(new_sock);
在服务器代码中,因为如果在这样的早期点释放它们,新创建的线程就不能对这些变量和内存区域执行。没有它你能再次尝试代码吗?
答案 2 :(得分:0)
您的服务器关闭连接的套接字,并在启动线程以处理该连接后立即释放存储其文件句柄的内存。你不幸的是服务器只是挂起了,但你有数据竞争,所以正式你的程序的行为是未定义的。
由于服务器在线程完成之前不会执行任何其他操作,因此您也可以移动close()
和free()
之后 {{ 1}}。或者,考虑到你在创建任何其他线程之前做加入,那么如何同步调用pthread_join()
而不是创建一个新的线程让它运行?