C Socket编程:Socket连接,但不能通过socket写到服务器
C Socket Programming: Socket connects, but cannot write through socket to server
我正在开发一个简单的 client/server 实现,其中客户端根据指定主机名的命令行参数连接到服务器,以搜索要连接的套接字(我只是输入 localhost ) 然后发送一个被服务器反转的单数字符串,然后将这个反转的字符串发送回客户端,并打印反转的字符串。我能够连接到位于本地主机(客户端)的套接字,但通过套接字写入失败,我不确定为什么。
这是服务器代码:它有一个内置的反向器功能,可以手动反向字符串,然后在读取客户端写入服务器的原始消息后通过套接字将其写回客户端
#include <netdb.h>
#define BACKLOG 20
#include "server.h"
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_MESSAGE_LENGTH 200
char* reverse(char * word){
char *reversed;
int m=strlen(word);
reversed=(char*)(malloc(sizeof(char) * m));
printf("%d",m);
int i=0;
while (i++!=m){
char d= *(word+m-i);
strncat(reversed,&d,1);
}
return reversed;
}
void reverser_response(int sockfd,char *write_buff,char *read_buff){
read(sockfd,read_buff,sizeof(read_buff));
char *reversed_message=reverse(read_buff);
int i=0;
while (*(reversed_message)!='[=10=]'){
write_buff[i]=*(reversed_message);
i++;
reversed_message=reversed_message+i;
}
write(sockfd,write_buff,sizeof(write_buff));
bzero(write_buff,sizeof(write_buff));
bzero(read_buff,sizeof(read_buff));
}
int main(){
int cfd,afd;
//I'm pretty sure service will be the port name
char service[NI_MAXSERV];
//hostname will be the name of the IP address
char host[NI_MAXHOST];
char read_buff[200];
char write_buff[200];
//I rmemember hints is used to set certain settings within the struct addrinfo result we create
struct addrinfo hints;
//this is used for looping through possible addrinfo structus
struct addrinfo *result, *rp;
//I think this stores the address of the client that connect with us
struct sockaddr_storage claddr;
//combined host + service name with padding of 10 bits
char addrstr[NI_MAXHOST+NI_MAXSERV+10];
memset(&hints,0,sizeof(struct addrinfo));
//socklen is the size of the socket
socklen_t socklen;
//I think AF_UNSPEC means that we can use an unspecified IP protocl: IPV4 or IPV6
hints.ai_family=AF_UNSPEC;
//stream socket
hints.ai_socktype=SOCK_STREAM;
hints.ai_next=NULL;
hints.ai_canonname=NULL;
hints.ai_addr=NULL;
//Passive: we wait for someone to join with our socket, numeric serv: use the numeric host name
hints.ai_flags=AI_PASSIVE | AI_NUMERICSERV;
//getadrrinfo: 0 is success, takes in as arguments, NULL(?) our port number, references to the hints and result addrinfo structs
//actually getaddrinfo generates a linked list of addrinfo structs for the specified host name/service name
//in this case, result is the head of the linkedlist, hints is the hints thing that sets csome conditions
if ((getaddrinfo(NULL,PORT_NUM,&hints,&result))!=0){
printf("Failed to get result pointer of address structs");
exit(EXIT_FAILURE);
}
//loop through possible addrinfo structs we can successfully create a socket at. Create socket at every possible struct. If we are successful
//in binding then quit. bind takes as arguments the socket's file descriptor, the address of the socket, and the lenth of the socket's address
//socket takes as arguments the addrinfo structu's ai_family (IPV4/6), socketpye (stream socket), and the protocol?
for (rp=result;rp;rp!=NULL){
cfd=socket(rp->ai_family,rp->ai_socktype, rp->ai_protocol);
if (cfd==-1){
continue;
}
//rp->ai_addr could be a pointer to a sockaddr_in or sockaddr_in6
if (bind(cfd,rp->ai_addr, rp->ai_addrlen)==0){
break;
}
}
if (rp==NULL){
printf("Reached end of address list without finding suitable socket address space");
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
if (listen(cfd,BACKLOG)==0){
printf("Server listening....\n");
}
for (;;){
socklen=sizeof(struct sockaddr);
afd=accept(cfd,(struct sockaddr*) &claddr,&socklen);
if (afd==-1){
perror("Accept failed");
}
getnameinfo((struct sockaddr*) &claddr,socklen,
host,NI_MAXHOST,service,NI_MAXSERV,0);
snprintf(addrstr,NI_MAXSERV+NI_MAXHOST+10, "Connection received from: (%s, %s)", host,service);
printf("%s\n",addrstr);
reverser_response(afd,write_buff,read_buff);
}
close(afd);
close(cfd);
}
这是服务器实现,它向服务器发送消息,然后通过套接字读取服务器发回的反向消息:
#include <netdb.h>
#include "server.h"
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "struct.h"
#define MAX_MESSAGE_LENGTH 200
void chat_function(int sockfd,char *write_buffer,char *read_buffer){
printf("\nEnter a message to send to the server. The server will reverse it, and send it back\n");
fscanf(stdin,"%s",write_buffer);
int m=strlen(write_buffer);
write_buffer[m]='[=11=]';
int i=0;
if (write(sockfd,write_buffer,sizeof(write_buffer))!=0){
printf("Failed to send message to server\n");
exit(EXIT_FAILURE);
}
read(sockfd,read_buffer,sizeof(read_buffer));
int j=strlen(read_buffer);
read_buffer[j]='[=11=]';
int z=0;
printf("Reversed server response:\n");
while (read_buffer[z]!='[=11=]'){
printf("%c",read_buffer[z]);
z++;
}
}
int main(int argc, char*argv[]){
struct addrinfo hints;
struct addrinfo *result, *rp;
int cfd;
socklen_t socklen;
memset(&hints,0,sizeof(struct addrinfo));
hints.ai_family=AF_UNSPEC; //can accept IPV4 or IPV6
hints.ai_socktype=SOCK_STREAM;
hints.ai_next=NULL;
hints.ai_canonname=NULL;
hints.ai_addr=NULL;
hints.ai_flags=AI_NUMERICSERV;
char addbuff[NI_MAXSERV+NI_MAXHOST+10];
char write_buffer[MAX_MESSAGE_LENGTH];
char read_buffer[MAX_MESSAGE_LENGTH];
bzero(write_buffer,sizeof(write_buffer));
bzero(read_buffer,sizeof(read_buffer));
if (argc<2){
printf("Failed to give hostname for client");
exit(EXIT_FAILURE);
}
if ((getaddrinfo(argv[1],PORT_NUM,&hints,&result))!=0){
printf("You did not provide a legitimate host name for the client socket to search through addresses to connect to");
exit(EXIT_FAILURE);
}
for (rp=result;rp;rp=rp->ai_next){
cfd=socket(rp->ai_family,rp->ai_socktype, rp->ai_protocol);
if (cfd==-1){
continue;
}
if (connect(cfd,rp->ai_addr,rp->ai_addrlen)!=-1){
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (cfd, SOL_SOCKET, SO_ERROR, &error, &len);
if (retval==0){
printf("Socket successfully connected\n");
}
break;
}
}
chat_function(cfd,write_buffer,read_buffer);
if (rp==NULL){
printf("Could not connect socket to any address");
exit(EXIT_FAILURE);
}
close(cfd);
}
将服务器 运行 作为后台进程,我尝试像这样写入服务器:
./client localhost (this connects the client to the socket that lives at localhost, the server)
当我实际执行客户端时,我收到代码中的错误消息,指示通过客户端中的套接字写入失败。此外,当向服务器的反转方法添加打印语句以输出它接收到的字符串的长度作为要反转的参数时,它将能够识别它接收到的字符串的字符串长度。这进一步让我感到困惑 - 如果我的客户端甚至无法通过套接字进行写入,服务器如何能够知道它应该反转的单词的长度?
尝试使用 send()
和 recv()
而不是 write()
和 read()
下一个问题是当你想获得超过四个字节时,这些函数大多数时候会失败,所以你需要做这样的事情:
(你不应该一次发送或接收所有这些。
// allocate 4 byte memory for Memory2Send
Remained_Data = i;
j = 0
while (Remained_Data > 0){
if(Remained_Data > 4){
memcpy(Memory2Send, Memory4Data + j, 4;
send(Sock, Memory2Send, 4, 0);
j = j + 4;
Remained_Data = Remained_Data - 4;
}
else if(Remained_Data < 4){
memcpy(Memory2Send, Memory4Data + j, 4;
send(Sock, Memory2Send, Remained_Data, 0);
Remained_Data = 0;
}
尝试每轮发送 4 个字节。你会得到所有的数据
对于服务器端,您需要每轮接收 4 个字节。
// allocate 4 byte memory for Memory4Recv
j = 1;
z = 0;
while (j != 0) {
bzero(Memory4Recv,4);
recv(Sock, Memory4Recv, 4, 0);
for (i = 0; i < 4; i++){
Recv_Data[z] = Memory4Recv[i];
if (Recv_Data[z] == 0x00){
j = 0;
break;
}
z++;
}
}
在保留内存的末尾放置一个空字节,如果大小等于i
发送,i + 1
;
如果你想更好地沟通,每轮发送超过 4 个字节并获得更可靠的连接,你应该在内核端进行。但首先,在 C 和用户端套接字编程方面做得更好,然后再去内核端。
我正在开发一个简单的 client/server 实现,其中客户端根据指定主机名的命令行参数连接到服务器,以搜索要连接的套接字(我只是输入 localhost ) 然后发送一个被服务器反转的单数字符串,然后将这个反转的字符串发送回客户端,并打印反转的字符串。我能够连接到位于本地主机(客户端)的套接字,但通过套接字写入失败,我不确定为什么。
这是服务器代码:它有一个内置的反向器功能,可以手动反向字符串,然后在读取客户端写入服务器的原始消息后通过套接字将其写回客户端
#include <netdb.h>
#define BACKLOG 20
#include "server.h"
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_MESSAGE_LENGTH 200
char* reverse(char * word){
char *reversed;
int m=strlen(word);
reversed=(char*)(malloc(sizeof(char) * m));
printf("%d",m);
int i=0;
while (i++!=m){
char d= *(word+m-i);
strncat(reversed,&d,1);
}
return reversed;
}
void reverser_response(int sockfd,char *write_buff,char *read_buff){
read(sockfd,read_buff,sizeof(read_buff));
char *reversed_message=reverse(read_buff);
int i=0;
while (*(reversed_message)!='[=10=]'){
write_buff[i]=*(reversed_message);
i++;
reversed_message=reversed_message+i;
}
write(sockfd,write_buff,sizeof(write_buff));
bzero(write_buff,sizeof(write_buff));
bzero(read_buff,sizeof(read_buff));
}
int main(){
int cfd,afd;
//I'm pretty sure service will be the port name
char service[NI_MAXSERV];
//hostname will be the name of the IP address
char host[NI_MAXHOST];
char read_buff[200];
char write_buff[200];
//I rmemember hints is used to set certain settings within the struct addrinfo result we create
struct addrinfo hints;
//this is used for looping through possible addrinfo structus
struct addrinfo *result, *rp;
//I think this stores the address of the client that connect with us
struct sockaddr_storage claddr;
//combined host + service name with padding of 10 bits
char addrstr[NI_MAXHOST+NI_MAXSERV+10];
memset(&hints,0,sizeof(struct addrinfo));
//socklen is the size of the socket
socklen_t socklen;
//I think AF_UNSPEC means that we can use an unspecified IP protocl: IPV4 or IPV6
hints.ai_family=AF_UNSPEC;
//stream socket
hints.ai_socktype=SOCK_STREAM;
hints.ai_next=NULL;
hints.ai_canonname=NULL;
hints.ai_addr=NULL;
//Passive: we wait for someone to join with our socket, numeric serv: use the numeric host name
hints.ai_flags=AI_PASSIVE | AI_NUMERICSERV;
//getadrrinfo: 0 is success, takes in as arguments, NULL(?) our port number, references to the hints and result addrinfo structs
//actually getaddrinfo generates a linked list of addrinfo structs for the specified host name/service name
//in this case, result is the head of the linkedlist, hints is the hints thing that sets csome conditions
if ((getaddrinfo(NULL,PORT_NUM,&hints,&result))!=0){
printf("Failed to get result pointer of address structs");
exit(EXIT_FAILURE);
}
//loop through possible addrinfo structs we can successfully create a socket at. Create socket at every possible struct. If we are successful
//in binding then quit. bind takes as arguments the socket's file descriptor, the address of the socket, and the lenth of the socket's address
//socket takes as arguments the addrinfo structu's ai_family (IPV4/6), socketpye (stream socket), and the protocol?
for (rp=result;rp;rp!=NULL){
cfd=socket(rp->ai_family,rp->ai_socktype, rp->ai_protocol);
if (cfd==-1){
continue;
}
//rp->ai_addr could be a pointer to a sockaddr_in or sockaddr_in6
if (bind(cfd,rp->ai_addr, rp->ai_addrlen)==0){
break;
}
}
if (rp==NULL){
printf("Reached end of address list without finding suitable socket address space");
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
if (listen(cfd,BACKLOG)==0){
printf("Server listening....\n");
}
for (;;){
socklen=sizeof(struct sockaddr);
afd=accept(cfd,(struct sockaddr*) &claddr,&socklen);
if (afd==-1){
perror("Accept failed");
}
getnameinfo((struct sockaddr*) &claddr,socklen,
host,NI_MAXHOST,service,NI_MAXSERV,0);
snprintf(addrstr,NI_MAXSERV+NI_MAXHOST+10, "Connection received from: (%s, %s)", host,service);
printf("%s\n",addrstr);
reverser_response(afd,write_buff,read_buff);
}
close(afd);
close(cfd);
}
这是服务器实现,它向服务器发送消息,然后通过套接字读取服务器发回的反向消息:
#include <netdb.h>
#include "server.h"
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "struct.h"
#define MAX_MESSAGE_LENGTH 200
void chat_function(int sockfd,char *write_buffer,char *read_buffer){
printf("\nEnter a message to send to the server. The server will reverse it, and send it back\n");
fscanf(stdin,"%s",write_buffer);
int m=strlen(write_buffer);
write_buffer[m]='[=11=]';
int i=0;
if (write(sockfd,write_buffer,sizeof(write_buffer))!=0){
printf("Failed to send message to server\n");
exit(EXIT_FAILURE);
}
read(sockfd,read_buffer,sizeof(read_buffer));
int j=strlen(read_buffer);
read_buffer[j]='[=11=]';
int z=0;
printf("Reversed server response:\n");
while (read_buffer[z]!='[=11=]'){
printf("%c",read_buffer[z]);
z++;
}
}
int main(int argc, char*argv[]){
struct addrinfo hints;
struct addrinfo *result, *rp;
int cfd;
socklen_t socklen;
memset(&hints,0,sizeof(struct addrinfo));
hints.ai_family=AF_UNSPEC; //can accept IPV4 or IPV6
hints.ai_socktype=SOCK_STREAM;
hints.ai_next=NULL;
hints.ai_canonname=NULL;
hints.ai_addr=NULL;
hints.ai_flags=AI_NUMERICSERV;
char addbuff[NI_MAXSERV+NI_MAXHOST+10];
char write_buffer[MAX_MESSAGE_LENGTH];
char read_buffer[MAX_MESSAGE_LENGTH];
bzero(write_buffer,sizeof(write_buffer));
bzero(read_buffer,sizeof(read_buffer));
if (argc<2){
printf("Failed to give hostname for client");
exit(EXIT_FAILURE);
}
if ((getaddrinfo(argv[1],PORT_NUM,&hints,&result))!=0){
printf("You did not provide a legitimate host name for the client socket to search through addresses to connect to");
exit(EXIT_FAILURE);
}
for (rp=result;rp;rp=rp->ai_next){
cfd=socket(rp->ai_family,rp->ai_socktype, rp->ai_protocol);
if (cfd==-1){
continue;
}
if (connect(cfd,rp->ai_addr,rp->ai_addrlen)!=-1){
int error = 0;
socklen_t len = sizeof (error);
int retval = getsockopt (cfd, SOL_SOCKET, SO_ERROR, &error, &len);
if (retval==0){
printf("Socket successfully connected\n");
}
break;
}
}
chat_function(cfd,write_buffer,read_buffer);
if (rp==NULL){
printf("Could not connect socket to any address");
exit(EXIT_FAILURE);
}
close(cfd);
}
将服务器 运行 作为后台进程,我尝试像这样写入服务器:
./client localhost (this connects the client to the socket that lives at localhost, the server)
当我实际执行客户端时,我收到代码中的错误消息,指示通过客户端中的套接字写入失败。此外,当向服务器的反转方法添加打印语句以输出它接收到的字符串的长度作为要反转的参数时,它将能够识别它接收到的字符串的字符串长度。这进一步让我感到困惑 - 如果我的客户端甚至无法通过套接字进行写入,服务器如何能够知道它应该反转的单词的长度?
尝试使用 send()
和 recv()
而不是 write()
和 read()
下一个问题是当你想获得超过四个字节时,这些函数大多数时候会失败,所以你需要做这样的事情:
(你不应该一次发送或接收所有这些。
// allocate 4 byte memory for Memory2Send
Remained_Data = i;
j = 0
while (Remained_Data > 0){
if(Remained_Data > 4){
memcpy(Memory2Send, Memory4Data + j, 4;
send(Sock, Memory2Send, 4, 0);
j = j + 4;
Remained_Data = Remained_Data - 4;
}
else if(Remained_Data < 4){
memcpy(Memory2Send, Memory4Data + j, 4;
send(Sock, Memory2Send, Remained_Data, 0);
Remained_Data = 0;
}
尝试每轮发送 4 个字节。你会得到所有的数据
对于服务器端,您需要每轮接收 4 个字节。
// allocate 4 byte memory for Memory4Recv
j = 1;
z = 0;
while (j != 0) {
bzero(Memory4Recv,4);
recv(Sock, Memory4Recv, 4, 0);
for (i = 0; i < 4; i++){
Recv_Data[z] = Memory4Recv[i];
if (Recv_Data[z] == 0x00){
j = 0;
break;
}
z++;
}
}
在保留内存的末尾放置一个空字节,如果大小等于i
发送,i + 1
;
如果你想更好地沟通,每轮发送超过 4 个字节并获得更可靠的连接,你应该在内核端进行。但首先,在 C 和用户端套接字编程方面做得更好,然后再去内核端。