在C客户机/服务器套接字 [英] Client/server sockets in c

查看:186
本文介绍了在C客户机/服务器套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我正在试图了解如何使用套接字进行C中的客户机/服务器程序,我一直在读围绕互联网的各种教程,希望尝试做可以处理在多个客户端一个小的反响服务器同时。每当客户端向服务器发送邮件时,服务器应该回声返回给客户端。我创建了code是无论从在学校的演讲(这解释了如何创建客户端和服务器)的教程的组合,以及一个例子,我发现这里的计算器(这表明如何使功能实际上呼应消息)。我希望有人能有什么我缺少正确地使这个工作方案向我解释。下面是客户端code:

 的#include<&stdio.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;
#包括LT&;&errno.h中GT;
#包括LT&; SYS / types.h中>
#包括LT&; SYS / socket.h中>焦炭BUF [80];
结构sockaddr MYNAME;无效replyBack(FILE * FP,诠释的sockfd){
炭sendline [1000],recvline [1000];
的printf(请输入您的回音:\\ n);
而(与fgets(sendline,1000,标准输入)!= NULL){
    写(的sockfd,sendline,sizeof的(sendline));
    如果(读(的sockfd,recvline,1000)== 0){
        的printf(str_cli:服务器终止prematurely);
        出口(-1);
    }
    的fputs(recvline,标准输出);
}
}主(){
INT袜子,adrlen,CNT;袜子=插座(AF_UNIX,SOCK_STREAM,0);
如果(袜子℃,){
    的printf(客户端套接字失败%d个\\ N,错误号);
    的printf(客户);
    出口(1);
}myname.sa_family = AF_UNIX;
的strcpy(myname.sa_data的/ tmp / billb);
adrlen = strlen的(myname.sa_data)+的sizeof(myname.sa_family);如果(连接(袜子,和放大器; MYNAME,adrlen)小于0){
    的printf(客户端连接失败%d个\\ N,错误号);
    PERROR(客户);
    出口(1);
}replyBack(标准输入,袜子);
出口(0);
}

这是服务器code:

 的#include<&stdio.h中GT;
#包括LT&;&errno.h中GT;
#包括LT&;&signal.h中GT;
#包括LT&;&stdlib.h中GT;
#包括LT&;&string.h中GT;
#包括LT&; SYS / types.h中>
#包括LT&; SYS / socket.h中>结构sockaddr MYNAME;
焦炭BUF [80];无效回波(INT的sockfd){
ssiz​​e_t供N;
INT write_err;
焦炭BUF [1000];
字符* send_start_pos;
而(1){
    bytes_in =读(的sockfd,BUF,1000);
    如果(bytes_in&所述; 1){
        如果(错误== EINTR)
            继续;
        打破;
    }
    bytes_remaining = bytes_in;
    send_start_pos = BUF;
    write_err = 0;    而((bytes_remaining大于0)及&放大器;!(write_err)){
        bytes_out =写(的sockfd,send_start_pos,
                bytes_remaining);
        如果(bytes_out℃,){
            如果(错误== EINTR)
                继续;
            write_err = 1;
            打破;
        }
    bytes_remaining - = bytes_out;
    send_start_pos + = bytes_out;
    }
    如果(write_err)
        打破;
}
}主(){
INT袜子,new_sd,adrlen,CNT;袜子=插座(AF_UNIX,SOCK_STREAM,0);
如果(袜子℃,){
    的printf(服务器套接字失败%d个\\ N,错误号);
    PERROR(服务器);
    出口(1);
}myname.sa_family = AF_UNIX;
的strcpy(myname.sa_data的/ tmp / billb);
adrlen = strlen的(myname.sa_data)+的sizeof(myname.sa_family);取消链接(/ tmp目录/ billb); / *防御性编程* /
如果(绑定(袜子,和放大器; MYNAME,adrlen)小于0){
    的printf(服务器绑定失败%d个\\ N,错误号);
    PERROR(服务器);
    出口(1);
}如果(听(袜子,5)℃,){
    的printf(服务器故障监听%d个\\ N,错误号);
    PERROR(服务器);
    出口(1);
}而(1){
    如果(new_sd =接受(袜子,和放大器; MYNAME,&安培; adrlen)小于0){
        的printf(服务器接受失败%d个\\ N,错误号);
        PERROR(服务器);
        出口(1);
    }    的printf(服务器%D接口,地址为%s%S \\ n,
        GETPID(),myname.sa_data,myname.sa_data);    如果(叉()== 0){
        关闭(袜子);
        回声(new_sd);
        出口(0);
    }
    关闭(new_sd);
}
}

问题,当我运行的程序是客户端所接受的消息,但随后实际上并没有将它发送给服务器,因此服务器不能呼应回来。

我知道这可能是基本的东西,所以我AP preciate你的耐心和时间!


解决方案

  adrlen = strlen的(myname.sa_data)+的sizeof(myname.sa_family);


这应该是的sizeof MYNAME 。在 UNIX(7)插座实际上是定义为具有 sockaddr_un 是这样的:

 的#define UNIX_PATH_MAX 108       结构sockaddr_un {
           sa_family_t sun_family; / * * AF_UNIX /
           焦炭sun_path [UNIX_PATH_MAX] / * *路径/
       };

您已经给它 10 + 4 的大小。几乎应该努力​​ - 它不会在文件名末尾算终止 NUL 字节 - 但即使与 + 1 补充,我会感觉更好,如果你传递你有对象的确切大小。 (小心的的strlen(3)的字符串。几乎总是需要一个 + 1 在任何Ex pression涉及的strlen(3)搞笑。)


 而(与fgets(sendline,1000,标准输入)!= NULL){
    写(的sockfd,sendline,sizeof的(sendline));


在这里,你将所有1000个字节的缓冲区,而不管内容。用户可能输入一个字符,还是一百个,而你发送所有1000个字节的一切就是在那个地址。

回声方法有点多;我建议你​​关闭分裂写入到它自己的程序; 高级编程在UNIX环境中,第二版(A 精湛的书籍,非常值得找到一个副本,如果你希望Unix或类Unix系统程序)有一个小甜例行我真的很喜欢:


  ssize_t供/ *写的n个字节的描述符* /
writen(INT FD,常量无效* PTR,为size_t N)
{
    为size_t nleft;
    ssiz​​e_t供nwritten;    nleft = N;
    而(nleft大于0){
        如果((nwritten =写(FD,PTR,nleft))小于0){
            如果(nleft == N)
                返回(-1); / *错误,返回-1 * /
            其他
                打破; / *错误,返回量至今写* /
        }如果别人(nwritten == 0){
            打破;
        }
        nleft - = nwritten;
        PTR + = nwritten;
    }
    回报(N - nleft); / *回报> = 0 * /
}


(见的lib / writen.c 源本书的网站)。

我还没有实际的尝试的您code,所以它是没有这些很可能是问题实际上是$ P $无法正常工作:)如果是这样的话,留个pventing软件发表评论,我会仔细看看。

I am currently trying to understand how to use sockets to make a client/server program in C. I have been reading various tutorials around the Internet in hopes to try and make a small echo server that can deal with multiple clients at the same time. Whenever the client sends the server a message, the server is supposed to echo is back to the client. The code that I created is a combination of both a tutorial from a lecture at school (which explained how to create the client and server) as well as an example I found on here on stackoverflow (which showed how to make the functions to actually echo the messages). I am hoping that someone can explain to me what I am missing to make this program work correctly. Here is the client code:

#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);
}

And here is the server code:

#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);
}
}

The problem when I run the program is that the client is accepting the message but then does not actually send it to the server, so the server can never echo it back.

I know this is probably basic stuff so I appreciate your patience and time!

解决方案

adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);

This should be sizeof myname. The unix(7) sockets are actually defined to have a sockaddr_un like this:

       #define UNIX_PATH_MAX    108

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

You've given it 10+4 as the size. Which should almost work -- it doesn't count the terminating NUL byte at the end of the filename -- but even with the + 1 added, I'd feel better if you're passing in exactly the size of object you have. (Be careful of strlen(3) on strings. Almost always you need a + 1 in any expression that involves strlen(3). Funny.)

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

And here you're sending all 1000 bytes of your buffer, regardless of contents. The user might have entered one character, or one hundred, and you're sending all 1000 bytes of whatever is at that address.

Your echo method is a bit much; I'd recommend splitting the writing off into its own routine; Advanced Programming in the Unix Environment, 2nd edition (a superb book, well worth finding a copy if you expect to program Unix or Unix-like systems) has a small sweet routine I really enjoy:

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 */
}

(See lib/writen.c in the source from the book's website.)

I haven't actually tried your code, so it's very possible none of these are the problem actually preventing your software from working :) if that's the case, leave a comment, and I'll take a closer look.

这篇关于在C客户机/服务器套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆