在 c 套接字编程中使用 write 发送缓冲区
Sending buffer in c socket programming with write
我正在用 fork()
在 C 中创建一个 TCP 聊天室。每个客户端消息都应该到达带有缓冲区中的用户名和消息的服务器,所以我使用 strcpy(buffer, name)
和 strcat(buffer, ": ")
来组合缓冲区和名称。之后我使用
n = write (sockfd, buffer, strlen(buffer))
向服务器发送缓冲区。所以就在这里发生了奇怪的事情,我的服务器什么也没显示,但是在那之后当我使用
n = read(sockfd, buffer, 255);
printf("%s", buffer);
字符串已正确传输。这是我的服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
int main(){
int port;
int i = 1;
printf("Enter port ->");
scanf("%d", &port);
int sockfd, newsockfd, n;
char buffer[255];
struct sockaddr_in6 serv_addr, cli_addr;
int ret, flag;
sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0){
printf("Error opening socket\n");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
flag = 1;
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
if (ret < 0){
printf("Set sock opt errror\n");
}
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_addr = in6addr_any;
serv_addr.sin6_port = htons(port);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
printf("Error binding\n");
}
listen(sockfd, 5);
int clilen = sizeof(cli_addr);
int pid;
while(1){
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0){
printf("Error on accept\n");
}
printf("New connection\n");
pid = fork();
if (pid < 0){
printf("Error on pid\n");
}
if (pid == 0){
close(sockfd);
while(1){
bzero(buffer, 255);
n = read(newsockfd, buffer, 255);
if (n < 0){
printf("Error on reading\n");
}
printf("%s", buffer);
n = write(newsockfd, buffer, strlen(buffer));
if (n < 0){
printf("Error on writing\n");
}
}
}
}
close(newsockfd);
return 0;
}
和客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(){
int port;
printf("Enter port ->");
scanf("%d", &port);
char ip[10];
printf("Enter ip -> ");
scanf("%s", &ip);
char name[20];
printf("Enter name -> ");
scanf("%s", &name);
int sockfd, n;
char buffer[255];
struct sockaddr_in6 serv_addr;
sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0){
printf("Error opening socket\n");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
inet_pton(AF_INET6, "ip", &serv_addr.sin6_addr);
serv_addr.sin6_port = htons(port);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
printf("Connection failed\n");
}
while(1){
bzero(buffer, 255);
fgets(buffer, 255, stdin);
strcpy(buffer, name);
strcat(buffer, ": ");
n = write (sockfd, buffer, strlen(buffer));
bzero(buffer,255);
if (n < 0){
printf("Error on writing\n");
}
n = read(sockfd, buffer, 255);
printf("%s", buffer);
if (n < 0){
printf("Error on reading\n");
}
}
close(sockfd);
}
所以也许有人能看出为什么客户端无法将包含名称和消息的完整缓冲区发送到服务器的问题?我认为问题出在 name
上。当我仅发送使用
获得的缓冲区时
fgets(buffer, 255, stdin);
服务器工作正常,但我需要客户端用户名。
我尝试测试 strlen(name)
和 buffer
,一切似乎都还好。我尝试做的是像这样手动将名称转换为缓冲区:
for (int i = 0; i < strlen(name); i++){
buffer[i] = name[i];
}
但是如果这是唯一的方法,我应该如何将缓冲区推送到正确的输入名称并且不丢失消息信息?
您的代码有很多错误:)。
首先 scanf
将指针作为第一个参数,所以当你想将它用于字符串时:
char ip[10];
...
scanf("%s", ip);//THIS IS CORRECT
不是scanf("%s", &ip);
。
在服务器上,编译器抱怨 clilen
的类型。 accept
期望 sockelen_t
(或等效的 unsigned int
),而您将该变量声明为简单的 int
.
主要问题
是下面的。在客户端中,你做了 strcpy(buffer, name);
从缓冲区的开头复制 name
但缓冲区的开头实际上包含你的数据。您应该像在下一行中那样使用 strcat
执行以下操作:
bzero(buffer, 255);
fgets(buffer, 255, stdin);
strcat(buffer, name);
strcat(buffer, ": ");
进一步的一般建议
还要注意另一件事:避免将系统调用函数 read
write
getchar
与 fread
、fwrite
、[=27 等库函数混合使用=].我有 的原因是 fgets
可能读取比预期更多的字节。在你的情况下应该没问题,因为你将它们用于不同的目的。
我正在用 fork()
在 C 中创建一个 TCP 聊天室。每个客户端消息都应该到达带有缓冲区中的用户名和消息的服务器,所以我使用 strcpy(buffer, name)
和 strcat(buffer, ": ")
来组合缓冲区和名称。之后我使用
n = write (sockfd, buffer, strlen(buffer))
向服务器发送缓冲区。所以就在这里发生了奇怪的事情,我的服务器什么也没显示,但是在那之后当我使用
n = read(sockfd, buffer, 255);
printf("%s", buffer);
字符串已正确传输。这是我的服务器代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
int main(){
int port;
int i = 1;
printf("Enter port ->");
scanf("%d", &port);
int sockfd, newsockfd, n;
char buffer[255];
struct sockaddr_in6 serv_addr, cli_addr;
int ret, flag;
sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0){
printf("Error opening socket\n");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
flag = 1;
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
if (ret < 0){
printf("Set sock opt errror\n");
}
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_addr = in6addr_any;
serv_addr.sin6_port = htons(port);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
printf("Error binding\n");
}
listen(sockfd, 5);
int clilen = sizeof(cli_addr);
int pid;
while(1){
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0){
printf("Error on accept\n");
}
printf("New connection\n");
pid = fork();
if (pid < 0){
printf("Error on pid\n");
}
if (pid == 0){
close(sockfd);
while(1){
bzero(buffer, 255);
n = read(newsockfd, buffer, 255);
if (n < 0){
printf("Error on reading\n");
}
printf("%s", buffer);
n = write(newsockfd, buffer, strlen(buffer));
if (n < 0){
printf("Error on writing\n");
}
}
}
}
close(newsockfd);
return 0;
}
和客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(){
int port;
printf("Enter port ->");
scanf("%d", &port);
char ip[10];
printf("Enter ip -> ");
scanf("%s", &ip);
char name[20];
printf("Enter name -> ");
scanf("%s", &name);
int sockfd, n;
char buffer[255];
struct sockaddr_in6 serv_addr;
sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0){
printf("Error opening socket\n");
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
inet_pton(AF_INET6, "ip", &serv_addr.sin6_addr);
serv_addr.sin6_port = htons(port);
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
printf("Connection failed\n");
}
while(1){
bzero(buffer, 255);
fgets(buffer, 255, stdin);
strcpy(buffer, name);
strcat(buffer, ": ");
n = write (sockfd, buffer, strlen(buffer));
bzero(buffer,255);
if (n < 0){
printf("Error on writing\n");
}
n = read(sockfd, buffer, 255);
printf("%s", buffer);
if (n < 0){
printf("Error on reading\n");
}
}
close(sockfd);
}
所以也许有人能看出为什么客户端无法将包含名称和消息的完整缓冲区发送到服务器的问题?我认为问题出在 name
上。当我仅发送使用
fgets(buffer, 255, stdin);
服务器工作正常,但我需要客户端用户名。
我尝试测试 strlen(name)
和 buffer
,一切似乎都还好。我尝试做的是像这样手动将名称转换为缓冲区:
for (int i = 0; i < strlen(name); i++){
buffer[i] = name[i];
}
但是如果这是唯一的方法,我应该如何将缓冲区推送到正确的输入名称并且不丢失消息信息?
您的代码有很多错误:)。
首先 scanf
将指针作为第一个参数,所以当你想将它用于字符串时:
char ip[10];
...
scanf("%s", ip);//THIS IS CORRECT
不是scanf("%s", &ip);
。
在服务器上,编译器抱怨 clilen
的类型。 accept
期望 sockelen_t
(或等效的 unsigned int
),而您将该变量声明为简单的 int
.
主要问题
是下面的。在客户端中,你做了 strcpy(buffer, name);
从缓冲区的开头复制 name
但缓冲区的开头实际上包含你的数据。您应该像在下一行中那样使用 strcat
执行以下操作:
bzero(buffer, 255);
fgets(buffer, 255, stdin);
strcat(buffer, name);
strcat(buffer, ": ");
进一步的一般建议
还要注意另一件事:避免将系统调用函数 read
write
getchar
与 fread
、fwrite
、[=27 等库函数混合使用=].我有 fgets
可能读取比预期更多的字节。在你的情况下应该没问题,因为你将它们用于不同的目的。