TCP回射服务器

版权声明:本文为博主原创文章,转载请备注https://blog.csdn.net/travelerwz。 https://blog.csdn.net/Travelerwz/article/details/82010820

TCP回射服务器

所谓的回射服务器就是说从客户端输入,经过服务器,然后在客户端输出,用一张图来表示最合理。

081520265032957这里写图片描述

这就是所谓的回射服务器,我们可以去看看《unix网络编程卷一》。

它的一个大概思路就是:

服务器是一个并发服务器,所以当连接到来时,会fork一个子进程来对客户请求进行处理。其他部分就按简单的客户-服务器通信的步骤来写就行,客户端:socket()—>connect()—>处理函数;服务器:socket()—>bind()—>listen()—>accept()—>处理函数

程序代码:

utili.h

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<signal.h>
#define LISTENQ 1024
#define SERV_PORT 9857

#define BUFSIZE 4096

ser.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<signal.h>
#define LISTENQ 1024
#define SERV_PORT 9857

#define BUFSIZE 4096
wz@wz-machine:~/linux/socket/2$ cat ser.c 
#include"utili.h"

void str_echo(int sockfd)
{
    ssize_t n;
    char buf[BUFSIZE];
    while((n=read(sockfd,buf,BUFSIZE)) > 0)
        write(sockfd,buf,n);
}
void sig_chld(int signo)
{
    pid_t pid;
    int stat;
    while((pid = waitpid(-1,&stat,WNOHANG))>0)
        printf("child %d terminated\n",pid);
    return ;
}
int main()
{
    int confd,listenfd;
    struct sockaddr_in cliaddr,servaddr;
    pid_t childpid;
    socklen_t clien;
    int status;
    char buf[BUFSIZE];

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    listenfd = socket(AF_INET,SOCK_STREAM,0);
    if(listenfd == -1)
        printf("socket error\n");
    status = bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    if(status == -1)
        printf("bind error\n");
    status = listen(listenfd,LISTENQ);
    signal(SIGCHLD,sig_chld);
    while(1)
    {
        clien = sizeof(cliaddr);
        confd = accept(listenfd,(struct sockaddr*)&cliaddr,&clien);
        if(confd == -1)
            printf("accept error\n");
        if((childpid = fork())==0)
        {
            printf("connet from %s,port %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,buf,sizeof(buf)),ntohs(cliaddr.sin_port));
            close(listenfd);
            str_echo(confd);
            close(confd);
            exit(0);
        }
        close(confd);
    }
//  return 0;
}

cli.c

#include"utili.h"

void str_cli(FILE* fp,int sockfd)
{
    printf("connect success\n");
    char send[BUFSIZE],recv[BUFSIZE];
    while(fgets(send,BUFSIZE,fp)!=NULL)
    {
        write(sockfd,send,strlen(send));
        read(sockfd,recv,BUFSIZE);
        fputs(recv,stdout);
        bzero(recv,BUFSIZE);
    }
}

int main(int argc,char **argv)
{
    int sockfd;
    struct sockaddr_in servaddr;
    socklen_t clien;
    int status;
    char buf[BUFSIZE];

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;
//  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET,argv[1],&servaddr.sin_addr);

    sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
        printf("socket error\n");
    status = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
    if(status == -1)
        printf("connecd error\n");
    str_cli(stdin,sockfd);
    return 0;
//  exit(0);
}

这里面用到了signal函数,捕获SIGCHLD ,用来处理僵尸进程,在函数中调用了waitpid来处理子进程。其中waitpid中值-1表示等待第一个终止的子进程 ,最常用的选项是WNOHANG,它告知内核在没有已终止子进程时不要阻塞。

在网络编程时可能会遇到的三种情况:

(1)当fork子进程时,必须捕获SIGCHLD信号。

(2)当捕获信号时,必须处理被中断的系统调用。

(3)SIGCHLD的信号处理函数必须正确编写,应使用waitpid函数以免留下僵死进程。

在客户端和服务器中传输注意事项

(1)不同的实现以不同的格式存储二进制数。(大端和小端)

(2)不同的实现在存储相同的C数据类型上可能存在差异。(32位系统和64位系统)

(3)不同的实现给结构打包的方式存在差异,这取决于各种数据类型所用的位数以及机器的对齐限制。

因此,穿越套接口传送二进制结构绝不是明智的选择。

解决这种数据格式问题有两个常用的方法:

(1)把所有的数值数据作为文本串来传递。当然这里假设客户和服务器主机具有相同的字符集。

(2)显示定义所支持数据类型的二进制格式(位数、大端或小端),并以这样的格式在客户和服务器之间传递所有数据。

参考:《unix网络编程卷1》

猜你喜欢

转载自blog.csdn.net/Travelerwz/article/details/82010820