我用C语言做了一个使用套接字的聊天应用程序。当我在同一台设备上运行服务器和客户端时,它工作正常。但是,当我在不同的设备上运行客户端和服务器时,客户端显示连接错误。这个问题与IP地址有关吗?
服务器端代码:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <signal.h>
#define MAX_CLIENTS 100
#define BUFFER_SZ 2048
static _Atomic unsigned int cli_count = 0;
static int uid = 10;
/* Client structure */
typedef struct{
struct sockaddr_in address;
int sockfd;
int uid;
char name[32];
} client_t;
client_t *clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
void str_overwrite_stdout() {
printf("\r%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void print_client_addr(struct sockaddr_in addr){
printf("%d.%d.%d.%d",
addr.sin_addr.s_addr & 0xff,
(addr.sin_addr.s_addr & 0xff00) >> 8,
(addr.sin_addr.s_addr & 0xff0000) >> 16,
(addr.sin_addr.s_addr & 0xff000000) >> 24);
}
/* Add clients to queue */
void queue_add(client_t *cl){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(!clients[i]){
clients[i] = cl;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Remove clients to queue */
void queue_remove(int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid == uid){
clients[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Send message to all clients except sender */
void send_message(char *s, int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i<MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid != uid){
if(write(clients[i]->sockfd, s, strlen(s)) < 0){
perror("ERROR: write to descriptor failed");
break;
}
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Handle all communication with the client */
void *handle_client(void *arg){
char buff_out[BUFFER_SZ];
char name[32];
int leave_flag = 0;
cli_count++;
client_t *cli = (client_t *)arg;
// Name
if(recv(cli->sockfd, name, 32, 0) <= 0 || strlen(name) < 2 || strlen(name) >= 32-1){
printf("Didn't enter the name.\n");
leave_flag = 1;
} else{
strcpy(cli->name, name);
sprintf(buff_out, "%s has joined\n", cli->name);
printf("%s", buff_out);
send_message(buff_out, cli->uid);
}
bzero(buff_out, BUFFER_SZ);
while(1){
if (leave_flag) {
break;
}
int receive = recv(cli->sockfd, buff_out, BUFFER_SZ, 0);
if (receive > 0){
if(strlen(buff_out) > 0){
send_message(buff_out, cli->uid);
str_trim_lf(buff_out, strlen(buff_out));
printf("%s -> %s\n", buff_out, cli->name);
}
} else if (receive == 0 || strcmp(buff_out, "exit") == 0){
sprintf(buff_out, "%s has left\n", cli->name);
printf("%s", buff_out);
send_message(buff_out, cli->uid);
leave_flag = 1;
} else {
printf("ERROR: -1\n");
leave_flag = 1;
}
bzero(buff_out, BUFFER_SZ);
}
/* Delete client from queue and yield thread */
close(cli->sockfd);
queue_remove(cli->uid);
free(cli);
cli_count--;
pthread_detach(pthread_self());
return NULL;
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
int option = 1;
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
pthread_t tid;
/* Socket settings */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip);
serv_addr.sin_port = htons(port);
/* Ignore pipe signals */
signal(SIGPIPE, SIG_IGN);
if(setsockopt(listenfd, SOL_SOCKET,(SO_REUSEPORT | SO_REUSEADDR),(char*)&option,sizeof(option)) < 0){
perror("ERROR: setsockopt failed");
return EXIT_FAILURE;
}
/* Bind */
if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR: Socket binding failed");
return EXIT_FAILURE;
}
/* Listen */
if (listen(listenfd, 10) < 0) {
perror("ERROR: Socket listening failed");
return EXIT_FAILURE;
}
printf("=== WELCOME TO THE CHATROOM ===\n");
while(1){
socklen_t clilen = sizeof(cli_addr);
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
/* Check if max clients is reached */
if((cli_count + 1) == MAX_CLIENTS){
printf("Max clients reached. Rejected: ");
print_client_addr(cli_addr);
printf(":%d\n", cli_addr.sin_port);
close(connfd);
continue;
}
/* Client settings */
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->address = cli_addr;
cli->sockfd = connfd;
cli->uid = uid++;
/* Add client to the queue and fork thread */
queue_add(cli);
pthread_create(&tid, NULL, &handle_client, (void*)cli);
/* Reduce CPU usage */
sleep(1);
}
return EXIT_SUCCESS;
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define LENGTH 2048
// Global variables
volatile sig_atomic_t flag = 0;
int sockfd = 0;
unsigned char name[32];
void str_overwrite_stdout() {
printf("%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void catch_ctrl_c_and_exit(int sig) {
flag = 1;
}
void send_msg_handler() {
unsigned char message[LENGTH] = {};
unsigned char buffer[3000] = {};
while(1) {
str_overwrite_stdout();
fgets(message, LENGTH, stdin);
str_trim_lf(message, LENGTH);
if (strcmp(message, "exit") == 0) {
break;
} else {
sprintf(buffer, "%s: %s\n", name, message);
send(sockfd, buffer, strlen(buffer), 0);
}
bzero(message, LENGTH);
bzero(buffer, LENGTH + 32);
}
catch_ctrl_c_and_exit(2);
}
void recv_msg_handler() {
char message[LENGTH] = {};
while (1) {
int receive = recv(sockfd, message, LENGTH, 0);
if (receive > 0) {
printf("%s", message);
str_overwrite_stdout();
} else if (receive == 0) {
break;
} else {
// -1
}
memset(message, 0, sizeof(message));
}
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "152.168.0.100";
int port = atoi(argv[1]);
signal(SIGINT, catch_ctrl_c_and_exit);
printf("Please enter your name: ");
fgets(name, 32, stdin);
str_trim_lf(name, strlen(name));
if (strlen(name) > 32 || strlen(name) < 2){
printf("Name must be less than 30 and more than 2 characters.\n");
return EXIT_FAILURE;
}
struct sockaddr_in server_addr;
/* Socket settings */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
// Connect to Server
int err = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err == -1) {
printf("ERROR: connect\n");
return EXIT_FAILURE;
}
// Send name
send(sockfd, name, 32, 0);
printf("=== WELCOME TO THE CHATROOM ===\n");
pthread_t send_msg_thread;
if(pthread_create(&send_msg_thread, NULL, (void *) send_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
pthread_t recv_msg_thread;
if(pthread_create(&recv_msg_thread, NULL, (void *) recv_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
while (1){
if(flag){
printf("\nBye\n");
break;
}
}
close(sockfd);
return EXIT_SUCCESS;
}
当我在不同的机器上运行它时,它显示连接错误。
1条答案
按热度按时间xqkwcwgp1#
据我所知,您发布的代码是不完整的,但让我们假设,正如您所说,一切都工作正常,它在本地计算机上工作,但当您在两个不同的设备上运行这两个程序时,客户端无法连接到服务器。
然后我会问你一个问题,就像我在评论中问的那样:这两个设备是否在同一子网中?2您有一个家用路由器,并且两个设备都连接到该路由器?
如果没有,那就是问题所在。我不知道你对IP地址和子网的理解有多深,但这里有一个简单的总结。当你查看你PC上的IP地址时,你会看到路由器给你的私有IP地址。假设你有两台机器连接到同一个路由器,一台的IP地址为www.example.com,另一台的IP地址为192.168.0.101192.168.0.102。如果客户端为192.168.0.101,并且要连接到在192.168.0.102上运行的服务器,则连接应该可以正常工作。
但是,假设您将服务器代码发送给我。我在自己的计算机上运行服务器,其IP地址为192.168.0.103,您仍然在家里的192.168.0.101上运行客户端。在这种情况下,您将无法连接到我的计算机,因为192.168.0.103是我自己的路由器提供的私有IP地址。要使其正常工作,我至少要配置我的路由器打开一个端口,并将来自该端口的数据包转发到我的PC。然后,我必须给予你公共IP和端口。因此,如果防火墙配置为允许你的数据包进入我的网络,那么它可能会工作。
当然比这要复杂一点,但这是大致的思路。
如果您的情况不是这样,并且设备位于同一子网,那么我们只需要更多信息和完整代码。