【问题标题】:Client/server sockets in cc中的客户端/服务器套接字
【发布时间】:2011-08-22 04:47:52
【问题描述】:

我目前正在尝试了解如何使用套接字在 C 中制作客户端/服务器程序。我一直在阅读 Internet 上的各种教程,希望尝试制作一个可以处理多个客户端的小型 echo 服务器同时。每当客户端向服务器发送消息时,服务器都应该回显给客户端。我创建的代码结合了学校讲座中的教程(解释了如何创建客户端和服务器)以及我在 stackoverflow 上找到的示例(展示了如何使函数实际回显)消息)。我希望有人可以向我解释我缺少什么以使该程序正常工作。这是客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>

char buf[80];
struct sockaddr myname;

void replyBack(FILE *fp, int sockfd) {
char sendline[1000], recvline[1000];
printf("Enter your echo: \n");
while(fgets(sendline,1000,stdin) != NULL) {
    write(sockfd,sendline,sizeof(sendline));
    if(read(sockfd,recvline,1000) == 0) {
        printf("str_cli: server terminated prematurely");
        exit(-1);
    }
    fputs(recvline, stdout);
}
}

main() {
int sock, adrlen, cnt;

sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
    printf("client socket failure%d\n", errno);
    printf("client: ");
    exit(1);
}

myname.sa_family = AF_UNIX;
strcpy(myname.sa_data, "/tmp/billb");
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);

if(connect(sock, &myname, adrlen) < 0) {
    printf("client connect failure %d\n", errno);
    perror("client: ");
    exit(1);
}

replyBack(stdin,sock);
exit(0);
}

这是服务器代码:

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>

struct sockaddr myname;
char buf[80];

void echo(int sockfd) {
ssize_t n;
int write_err;
char buf[1000];
char * send_start_pos;
while(1) {
    bytes_in = read(sockfd, buf, 1000);
    if(bytes_in < 1) {
        if(errno == EINTR)
            continue;
        break;
    }
    bytes_remaining = bytes_in;
    send_start_pos = buf;
    write_err = 0;

    while((bytes_remaining > 0) && !(write_err)) {
        bytes_out = write(sockfd, send_start_pos,           
                bytes_remaining);
        if(bytes_out < 0) {
            if(errno == EINTR)
                continue;
            write_err = 1;
            break;
        }
    bytes_remaining -= bytes_out;
    send_start_pos += bytes_out;
    }
    if(write_err)
        break;
}
}

main() {
int sock, new_sd, adrlen, cnt;

sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
    printf("server socket failure %d\n", errno);
    perror("server: ");
    exit(1);
}

myname.sa_family = AF_UNIX;
strcpy(myname.sa_data, "/tmp/billb");
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);

unlink("/tmp/billb"); /*defensive programming */
if(bind(sock, &myname, adrlen) < 0) {
    printf("server bind failure%d\n", errno);
    perror("server: ");
    exit(1);
}

if(listen(sock, 5) < 0) {
    printf("server listen failure %d\n", errno);
    perror("server: ");
    exit(1);
}

while(1) {
    if(new_sd = accept(sock, &myname, &adrlen) < 0) {
        printf("server accept failure %d\n", errno);
        perror("server: ");
        exit(1);
    }

    printf("Socket address in server %d is %s, %s\n", 
        getpid(), myname.sa_data, myname.sa_data);

    if(fork() == 0) {
        close(sock);
        echo(new_sd);
        exit(0);
    }
    close(new_sd);
}
}

我运行程序时的问题是客户端正在接受消息,但实际上并没有将其发送到服务器,因此服务器永远无法回显它。

我知道这可能是基本的东西,所以感谢您的耐心和时间!

【问题讨论】:

  • 你没有说是什么问题。
  • 在底部对其进行了编辑,希望使其更清晰,谢谢:)

标签: c sockets client-server echo


【解决方案1】:

您缺少一卡车的包含。我花时间编译了你的程序,这个 gem 从众多警告中脱颖而出

warning: suggest parentheses around assignment used as truth value

在这一行

if(new_sd = accept(sock, &myname, &adrlen) < 0) {

所以基本上你是在检查接受是否返回低于 0 的值并将其分配给 new_sd。将该行更改为

if((new_sd = accept(sock, &myname, &adrlen)) < 0) {

奖励: @sarnold 在他的回答中所说的大部分内容都是正确的,从长远来看,你会比我的回答做得更好。

【讨论】:

  • 哈!我不能告诉你我自己犯了多少次这个错字。接得好! :)
【解决方案2】:
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);

这应该是sizeof mynameunix(7) 套接字实际上被定义为具有这样的sockaddr_un

       #define UNIX_PATH_MAX    108

       struct sockaddr_un {
           sa_family_t sun_family;               /* AF_UNIX */
           char        sun_path[UNIX_PATH_MAX];  /* pathname */
       };

您已将其指定为 10+4 作为大小。这应该几乎可以工作 - 它不计算文件名末尾的终止 NUL 字节 - 但即使添加了 + 1,如果你传递的正是对象的大小,我会感觉更好你有。 (注意字符串上的strlen(3)。在涉及strlen(3) 的任何表达式中几乎总是需要+ 1。很有趣。)

while(fgets(sendline,1000,stdin) != NULL) {
    write(sockfd,sendline,sizeof(sendline));

在这里,无论内容如何,​​您都将发送缓冲区的所有 1000 个字节。用户可能输入了一个字符或一百个字符,而您正在发送该地址的所有 1000 个字节。

你的echo 方法有点多;我建议将注销分成自己的例程; Advanced Programming in the Unix Environment, 2nd edition(一本超棒的书,如果您希望编写 Unix 或类 Unix 系统,非常值得找一本)有一个我非常喜欢的小程序:

ssize_t             /* Write "n" bytes to a descriptor  */
writen(int fd, const void *ptr, size_t n)
{
    size_t      nleft;
    ssize_t     nwritten;

    nleft = n;
    while (nleft > 0) {
        if ((nwritten = write(fd, ptr, nleft)) < 0) {
            if (nleft == n)
                return(-1); /* error, return -1 */
            else
                break;      /* error, return amount written so far */
        } else if (nwritten == 0) {
            break;
        }
        nleft -= nwritten;
        ptr   += nwritten;
    }
    return(n - nleft);      /* return >= 0 */
}

(参见source from the book's website 中的lib/writen.c。)

我实际上并没有尝试过您的代码,因此很可能这些都不是真正阻止您的软件工作的问题 :) 如果是这种情况,请发表评论,我会仔细看看。

【讨论】:

  • 所以不是发送整个缓冲区,而是使用 strlen(sendline) 之类的东西吗?这是有道理的:)
  • 只是想让您知道,由于您的详细回答,我能够让我的程序正常工作,所以我觉得我现在对套接字的理解已经足够好,可以尝试从头开始编写我自己的客户端/服务器。我们会看看结果如何!
  • @A D,非常好,很高兴听到这个消息 :) 但不要忽视@cnicutar,他发现了阻止它工作的错误。 :)
猜你喜欢
  • 2017-05-26
  • 2014-03-10
  • 2015-09-07
  • 2012-06-24
  • 1970-01-01
  • 2012-05-18
  • 2011-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多