[英]C socket programming - Read and Write of TCP Server and Client out of sync
[英]C socket programming - write() from server writes to server instead of client
我正在研究一個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
您的代碼有很多問題。
在客戶端:
調用fread()
,需要使用"rb"
而不是"r"
。
當調用printf()
輸出實際讀取的文件數據時,您不是以null結尾的buffer
,也不是將其length
傳遞給printf()
。 您需要這樣做。
您正在將htonl()
的返回值分配給int
而不是unsigned int
。
當調用write()
發送buffer
時,您應該使用sizeof(buffer)
來代替使用length
或N
(為什么要使用兩個單獨的變量來保存相同的命令行參數值?)。 buffer
是指向通過malloc()
分配的內存的指針,因此sizeof(buffer)
與sizeof(void*)
,這不是您想要的。 同樣,您甚至都沒有正確調用write()
,因為您的括號全都錯了(在發送convertedNum
時,它們在上一個write()
調用中是正確的)。
同樣,當調用read()
讀取recvBuf
時,如果應使用sizeof(recvBuf)
,而應使用10
,則sicne recvBuf
也是指向已分配malloc
的指針。
您沒有讀取服務器在連接時發送給客戶端的"hi"
問候,因此您將這些字節與下一條消息的以下size值的字節混為一談,從而導致C
值損壞。
在服務器端:
您的compute
線程例程將"hi"
問候語發送給客戶端,但它不使用任何類型的定界符(例如,以問候語的長度作為前綴,或以換行符,空字符或其他唯一字符將其終止)來將其分隔從任何后續數據。 您應該始終以某種方式分隔消息。
創建工作線程來處理該客戶端后,您將關閉accept
套接字並釋放malloc
new_sock
。 您正在從線程眾所周知的后面剝離內存。 使用完線程后,線程必須是關閉套接字並釋放內存的線程,而不是accept
循環。
線程確實嘗試關閉套接字(但不釋放內存),但是在它首先調用pthread_exit()
,這是錯誤的。 pthread_exit()
終止了調用線程,因此它必須是線程調用的最后一件事(不要調用exit()
!)。 實際上,甚至根本不直接調用pthread_exit()
,僅從compute()
return
,然后pthreads庫將為您調用pthread_exit()
,並將您選擇return
任何void*
值傳遞給它。
您的accept
循環根本不應該調用pthread_join()
。 它阻塞調用線程,直到指定線程終止。 這破壞了使用線程來處理您的客戶端的整個目的,並阻止您的服務器一次接受一個以上的客戶端。 如果要使用pthread_join()
,則應該在accept
循環結束之后使用,因此可以在退出應用程序之前等待可能仍在運行的任何輔助線程。 但這還意味着跟蹤pthread_create()
返回的pthread_t
值,這需要更多工作。
話雖如此,請嘗試以下代碼:
客戶代碼:
#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;
}
服務器代碼:
#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>
#include <pthread.h>
#include <signal.h>
static volatile int keepRunning = 1;
int pcc_total[159];
void intHandler(int dummy) {
keepRunning = 0;
}
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;
}
void *compute(void *socket_desc) {
int sock = * (int*) socket_desc;
free(socket_desc);
if (sendMsg(sock, "hi", 2) != 0) {
perror("error writing to socket\n");
close(sock);
return NULL;
}
size_t length = 0;
char *data = readMsg(sock, &length);
if (!msg) {
close(sock);
return NULL;
}
printf("len is %d\n", (int)length);
printf("data is %.*s\n", (int)length, data);
int count = 0;
for (size_t i = 0; i < length; i++) {
// ...
}
free(data);
char scount[20];
sprintf(scount, "%d", count);
sendMsg(sock, scount, strlen(scount));
close(sock);
return NULL;
}
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;
socket_desc = socket( AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1) {
perror("Could not create server socket");
return 1;
}
printf("server 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 (bind(socket_desc, (struct sockaddr*) &server, sizeof(server)) < 0) {
perror("\n Error : Bind Failed \n");
close(socket_desc);
return 1;
}
printf("bind created\n");
if (listen(socket_desc, 10) < 0) {
perror("\n Error : Listen Failed \n");
close(socket_desc);
return 1;
}
printf("listening\n");
while (keepRunning) {
c = sizeof(client);
client_sock = accept(socket_desc, (struct sockaddr *) &client,
(socklen_t*) &c);
if (client_sock < 0) {
perror("\n Error : Accept Failed\n");
continue;
}
printf("client accepted\n");
new_sock = malloc(sizeof(int));
if (!new_sock) {
perror("\n Error : Malloc Failed\n");
close(client_sock);
continue;
}
*new_sock = client_sock;
pthread_t tid;
if (pthread_create(&tid, NULL, &compute, new_sock) != 0) {
perror("\n Error : Thread Create Failed\n");
free(new_sock);
close(client_sock);
continue;
}
printf("thread created\n");
}
close(socket_desc);
return 0;
}
我認為您應該刪除這兩行
close(client_sock);
free(new_sock);
在服務器代碼中,因為如果在這么早的時間釋放了新創建的線程,它們將無法在這些變量和內存區域上執行。 如果沒有它,您可以再次嘗試代碼嗎?
服務器在啟動處理該連接的線程后立即關閉連接的套接字並釋放存儲文件句柄的內存。 您很不幸服務器只會因此而掛起,但是您遇到了數據爭用問題,因此正式而言,程序的行為是不確定的。
由於服務器在線程完成之前不會執行任何其他操作,因此您最好將pthread_join()
之后的close()
和free()
移到該線程上。 或者,考慮到您確實在創建任何其他線程之前就加入了,如何僅同步調用compute()
而不是創建一個新線程以使其運行?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.