[英]C socket programming - Read and Write of TCP Server and Client out of sync
[英]Server proceeding without waiting to read client command in C socket programming
我有一個用 C 編寫的簡單服務器和客戶端。
他們溝通良好,直到我的程序結束,服務器似乎跳過“讀取”方法並繼續進行,它會在
printf("%s", playAgain);
這是代碼的結尾
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#define BACKLOG 10
char invalidPortNumber[] = "Please specify a port number between 2000 and 65535";
char intro[] = "Welcome to the prisoners dilemma";
char playGame[] = "Will you stay silent or betray the other prisoner?\nType S for silent or B for betray";
char option1[] = "The other prisoner betrayed you\nYou each get 2 years in prison";
char option2[] = "The other prisioner betrayed you\nYou get 3 years in prison, the other prisioner is set free";
char option3[] = "The other prisioner stayed silent\nYou are set free, the other prisioner gets 3 years in prison";
char option4[] = "The other prisioner stayed silent\nYou both get 1 year on a lesser charge";
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Run with port number as the argument\n");
exit(1);
}
int port = atoi(argv[1]);
if (port<2000 || port>65535){
printf("%s\n", invalidPortNumber);
exit(2);
}
//Struct to store information for IPv4 address
struct sockaddr_in serverAddress;
//Create socket for IPv4, reliable stream (TCP), default protocol
int serverSocket = socket(PF_INET, SOCK_STREAM, 0);
//Specify that IPv4 family addresses will be used
serverAddress.sin_family = AF_INET;
//Set the port number
serverAddress.sin_port = htons(port);
//Bind to all local interfaces for IP
serverAddress.sin_addr.s_addr = INADDR_ANY;
//Bind the created socket to the IP address specified in the sockaddr_in struct
bind(serverSocket, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
//Listen for connections, allowing backlog of up to BACKLOG connection requests
listen(serverSocket, BACKLOG);
int play = 0;
while(1) {
//Struct to store info of connecting clients
struct sockaddr_in clientAddress;
socklen_t clientAddrSize = sizeof(clientAddress);
//Create a socket for the connection between the client and server
int connectionSocket = accept(serverSocket, (struct sockaddr *) &clientAddress, &clientAddrSize);
//Input buffer to store client's request
do{
char input[800];
memset(input, '\0', sizeof(input));
//Have intro to the game
write(connectionSocket, intro, sizeof(intro) - 1);
//Read client's input
read(connectionSocket, input, sizeof(input)-1);
if(strcmp(input,"Y\n")==0||strcmp(input,"y\n")==0){
write(connectionSocket, playGame, sizeof(playGame) - 1);
}
else if(strcmp(input,"N\n")==0||strcmp(input,"n\n")==0){
write(connectionSocket, "Okay, connection closed", sizeof("Okay, connection closed") - 1);
close(connectionSocket);
return 0;
}
//read clients choice
char clientChoice[2];
read(connectionSocket, clientChoice, sizeof(clientChoice)-1);
srand(time(NULL));
int random = rand();
if( random % 2 ==0 ){
char serverChoice[2] = "S";
if(strcmp(clientChoice, "S")==0){
write(connectionSocket, option4, sizeof(option4) - 1);
}
else if(strcmp(clientChoice, "B")==0){
write(connectionSocket, option3, sizeof(option3) - 1);
}
}
else {
char serverChoice[2] = "B";
if(strcmp(clientChoice, "S")==0){
write(connectionSocket, option2, sizeof(option2) - 1);
}
else if(strcmp(clientChoice, "B")==0){
write(connectionSocket, option1, sizeof(option1) - 1);
}
}
char playAgain[5];
read(connectionSocket, playAgain, sizeof(playAgain)-1);
printf("%s",playAgain);
if(strcmp(playAgain, "Play")==0){
printf("Playing again");
play=1;
}
}while(play==1);
}
//Close the server socket and terminate the program if the loop ever ends
close(serverSocket);
return 0;
}
那就是服務器。
現在這是客戶端的結尾
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#define BACKLOG 10
char invalidPortNumber[] = "Please specify a port number between 2000 and 65535";
char intro[] = "Welcome to the prisoners dilemma";
int main(int argc, char *argv[]) {
char buffer[512];
char IPAddress[15];
int n;
if (argc < 2) {
printf("Run with host IP and port number as the argument\n");
exit(1);
}
int port = atoi(argv[1]);
if (port<2000 || port>65535){
printf("%s\n", invalidPortNumber);
exit(2);
}
//Struct to store information for IPv4 address
struct sockaddr_in serverAddress;
//Create socket for IPv4, reliable stream (TCP), default protocol
int serverSocket = socket(PF_INET, SOCK_STREAM, 0);
//Specify that IPv4 family addresses will be used
serverAddress.sin_family = AF_INET;
//Set the port number
serverAddress.sin_port = htons(port);
//Bind to all local interfaces for IP
serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1");
//Bind the created socket to the IP address specified in the sockaddr_in struct
int play=0;
if(connect(serverSocket, (struct sockaddr *) &serverAddress, sizeof(serverAddress))<0){
printf("Couldn't connect, make sure the server is running and port number is correct \n");
return 1;
}
//read intro from server
do{
bzero(buffer,512);
n = read(serverSocket,buffer,511);
printf("%s\n",buffer);
//ask user if they'd like to play
int validCommand=1;
do{
printf("Would you like to play? (Y/N) ");
bzero(buffer,512);
fgets(buffer,511,stdin);
if(strcmp(buffer, "Y\n")==0||strcmp(buffer, "N\n")==0){
validCommand=0;
}
else{
printf("Invalid command \n");
}
}while(validCommand==1);
//write whether user wants to play to server
n = write(serverSocket,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,512);
//read response from server
n = read(serverSocket,buffer,511);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
if(strcmp(buffer, "Okay, connection closed")==0){
close(serverSocket);
return 0;
}
do{
bzero(buffer,512);
printf("Make your choice (B/S) ");
fgets(buffer,511,stdin);
if(strcmp(buffer, "B\n")==0||strcmp(buffer, "S\n")==0){
validCommand=0;
}
else{
printf("Invalid command \n");
validCommand=1;
}
}while(validCommand==1);
//write the users choice to the server
n = write(serverSocket,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,512);
n = read(serverSocket,buffer,511);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
do{
bzero(buffer,512);
printf("Would you like to play again? (Play/Quit) ");
fgets(buffer,511,stdin);
if(strcmp(buffer, "Play\n")==0||strcmp(buffer, "Quit\n")==0){
validCommand=0;
}
else{
printf("Invalid command \n");
validCommand=1;
}
}while(validCommand==1);
//write the users choice to the server
if(strcmp(buffer, "Quit\n")==0){
printf("Closing Connection to server");
close(serverSocket);
return 0;
}
if(strcmp(buffer, "Play\n")==0){
printf("Playing again");
play=1;
n = write(serverSocket,buffer,strlen(buffer)-1);
if (n < 0)
error("ERROR writing to socket");
}
}while(play==1);
}
客戶端和服務器都為Choice B/S 工作,客戶端發送,服務器響應。 我不知道出了什么問題,但服務器似乎沒有等待客戶端的最終命令
首先,我認為您遇到的基本問題是一種常見的誤解,即 1 次寫入自動對應 1 次讀取。 它沒有。
你提到的問題是你的讀寫不同步造成的。 您需要確保每次讀取的數量與發送的數量相同。 服務器不是“在不等待讀取客戶端命令的情況下繼續進行”; 它剛剛閱讀並忽略了它。
例如,當客戶端執行
write(serverSocket, buffer, strlen(buffer))
服務器會感到困惑。 如果您不先發送超過字符串的大小,服務器將無法知道何時停止讀取。 這尤其正確,因為您不發送 NUL 終止符。 通過在客戶端進行更多處理可以避免這個特定問題。 通過檢查客戶端上的"Y"
和"N"
輸入,您可以將通信簡化為簡單地發送一個字節的布爾值。 這降低了代碼的復雜性以及服務器和客戶端之間所需的通信量。 如果您想了解如何開始改進這一點的示例,或者有疑問,請在評論中提問。
旁注:
1)您不需要通過套接字發送介紹; 它已經在客戶端。
2)像validCommand
這樣的布爾變量validCommand
是0
表示假, 1
表示真。 你似乎在你的代碼中翻轉了這個。 這本身並沒有錯,只是讀起來令人困惑。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.