`
kmplayer
  • 浏览: 500369 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

关于echo服务端和客户端

阅读更多
1,实现的基本功能:
客户端:发送一行文本给服务器,服务器显示收到的字节数,并返回收到的内容给客户端。

2,一个单进程的实现实例:
file echo.c:
#include "csapp.h"

void echo(int connfd) 
{
    size_t n; 
    char buf[MAXLINE]; 
    rio_t rio;

    Rio_readinitb(&rio, connfd);
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) 
	{ //line:netp:echo:eof
		printf("server received %d bytes\n", n);
		Rio_writen(connfd, buf, n);
    }
}

file echoclient.c:
#include "csapp.h"

int main(int argc, char **argv) 
{
    int clientfd, port;
    char *host, buf[MAXLINE];
    rio_t rio;

    if (argc != 3) 
	{
		fprintf(stderr, "usage: %s <host> <port>\n", argv[0]);
		exit(0);
    }
    host = argv[1];
    port = atoi(argv[2]);

    clientfd = Open_clientfd(host, port);
    Rio_readinitb(&rio, clientfd);

    while (Fgets(buf, MAXLINE, stdin) != NULL) 
	{
		Rio_writen(clientfd, buf, strlen(buf));
		Rio_readlineb(&rio, buf, MAXLINE);
		Fputs(buf, stdout);
    }
    Close(clientfd); //line:netp:echoclient:close
    exit(0);
}

file: echoserver.c
#include "csapp.h"

void echo(int connfd);

int main(int argc, char **argv) 
{
    int listenfd, connfd, port, clientlen;
    struct sockaddr_in clientaddr;
    struct hostent *hp;
    char *haddrp;
    if (argc != 2) 
	{
		fprintf(stderr, "usage: %s <port>\n", argv[0]);
		exit(0);
    }
    port = atoi(argv[1]);

    listenfd = Open_listenfd(port);
    while (1) 
	{
		clientlen = sizeof(clientaddr);
		connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);

		/* determine the domain name and IP address of the client */
		hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr, 
			   	sizeof(clientaddr.sin_addr.s_addr), AF_INET);
		haddrp = inet_ntoa(clientaddr.sin_addr);
		printf("server connected to %s (%s)\n", hp->h_name, haddrp);

		echo(connfd);
		Close(connfd);
    }
    exit(0);
}

注:此时,只能一次处理一个客户端,当第二个客户端加入时,被阻塞。
使用:
服务端:
./echoserver 8080
server connected to kmplayer (127.0.0.1)
server received 16 bytes
server received 14 bytes
server received 5 bytes
客户端:
./echoserver 8080
server connected to kmplayer (127.0.0.1)
server received 16 bytes
server received 14 bytes
server received 5 bytes

3,
基于进程的并发echo服务器:
#include "csapp.h"

void echo(int connfd);

void sigchld_handler(int sig)
{
    while (waitpid(-1, 0, WNOHANG) > 0) //准备好接受多个僵死进程
        ;
    return;
}

int main(int argc, char **argv)
{
    int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);
    struct sockaddr_in clientaddr;
    struct hostent *hp;
    char *haddrp;
    if (argc != 2)
	{
		fprintf(stderr, "usage: %s <port>\n", argv[0]);
		exit(0);
    }
    port = atoi(argv[1]);
    Signal(SIGCHLD, sigchld_handler); //回收子进程

    listenfd = Open_listenfd(port);
    while (1)
	{
		connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);

		if (Fork() == 0)
		{
		    Close(listenfd); //关闭复制过来的监听描述符
            hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
			   	sizeof(clientaddr.sin_addr.s_addr), AF_INET);
            haddrp = inet_ntoa(clientaddr.sin_addr);
            printf("server connected to %s (%s)\n", hp->h_name, haddrp);
            echo(connfd);
            Close(connfd);
            exit(0);
		}

		Close(connfd);
    }
    exit(0);
}

4,基于I/O多路复用,实现了标准输入和客户端的echo服务器
/*
 * echoserveri.c - An iterative echo server
 */
/* $begin echoserverimain */
#include "csapp.h"

void echo(int connfd);

void command(void);

int main(int argc, char **argv)
{
    int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);
    struct sockaddr_in clientaddr;
    struct hostent *hp;
    char *haddrp;
    fd_set read_set,ready_set;
    if (argc != 2)
	{
		fprintf(stderr, "usage: %s <port>\n", argv[0]);
		exit(0);
    }
    port = atoi(argv[1]);
    listenfd = Open_listenfd(port);

    FD_ZERO(&read_set);
    FD_SET(STDIN_FILENO, &read_set);
    FD_SET(listenfd, &read_set);
    while (1)
	{
	    ready_set=read_set;//重新载入
	    Select(listenfd+1, &ready_set, NULL, NULL, NULL); //挂起进程

	    if(FD_ISSET(STDIN_FILENO, &ready_set))
	    {
	        printf("server connected to local stdin.\n");
	        command();
	    }
	    if(FD_ISSET(listenfd, &ready_set))
	    {
            connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
            hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
            sizeof(clientaddr.sin_addr.s_addr), AF_INET);
            haddrp = inet_ntoa(clientaddr.sin_addr);
            printf("server connected to %s (%s)\n", hp->h_name, haddrp);
            echo(connfd);
            Close(connfd);
	    }
    }
    exit(0);
}

void command(void)
{
    char buf[MAXLINE];
    if (!Fgets(buf, MAXLINE, stdin))
        exit(0);
    printf("%s", buf);
}

注:一旦服务器连接到一个客户端,就会连续回送输入行,直到客户端关闭。
期间,你键入命令到标准输入将不会得到响应。

5,基于I/O多路复用,实现了多个客户端的echo服务器
/*
 * echoserveri.c - An iterative echo server
 */
/* $begin echoserverimain */
#include "csapp.h"

typedef struct
{
	int maxfd;
	fd_set read_set;
	fd_set ready_set;
	int nready;
	int maxi;
	int clientfd[FD_SETSIZE];
	rio_t clientrio[FD_SETSIZE];
}pool;

void init_pool(int listenfd, pool* p);
void add_client(int connfd, pool* p);
void check_clients(pool* p);

int byte_cnt=0;

int main(int argc, char **argv)
{
	int listenfd, connfd, port, clientlen = sizeof(struct sockaddr_in);
    struct sockaddr_in clientaddr;
    struct hostent *hp;
	static pool pool;
    char *haddrp;
    fd_set read_set,ready_set;
    if (argc != 2)
	{
		fprintf(stderr, "usage: %s <port>\n", argv[0]);
		exit(0);
    }
    port = atoi(argv[1]);
    listenfd = Open_listenfd(port);
	init_pool(listenfd, &pool);

    while (1)
	{
	    pool.ready_set = pool.read_set;//重新载入
		pool.nready = Select(pool.maxfd+1, &pool.ready_set, NULL, NULL, NULL);
	    if(FD_ISSET(listenfd, &pool.ready_set))
	    {
			connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);

            hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
                                sizeof(clientaddr.sin_addr.s_addr), AF_INET);
            haddrp = inet_ntoa(clientaddr.sin_addr);
            printf("server connected to %s (%s)\n", hp->h_name, haddrp);
			add_client(connfd, &pool);
	    }
		check_clients(&pool);
    }
    exit(0);
}
void init_pool(int listenfd, pool* p)
{
	int i;
	p->maxi = -1;
	for(i=0; i<FD_SETSIZE; i++)
		p->clientfd[i] = -1;
	p->maxfd = listenfd;
	FD_ZERO(&p->read_set);
	FD_SET(listenfd, &p->read_set);
}
void add_client(int connfd, pool* p)
{
	int i;
	p->nready--;
	for (i = 0; i < FD_SETSIZE; i++)
		if (p->clientfd[i] < 0)
		{
			p->clientfd[i] = connfd;
			Rio_readinitb(&p->clientrio[i], connfd);
			FD_SET(connfd, &p->read_set);
			if (connfd > p->maxfd)
				p->maxfd = connfd;
			if (i > p->maxi)
				p->maxi = i;
			break;
		}
		if(i == FD_SETSIZE)
			app_error("add_client error: Too many clients");
}
void check_clients(pool* p)
{
	int i, connfd, n;
	char buf[MAXLINE];
	rio_t rio;
	for (i = 0; (i <= p->maxi) && (p->nready > 0); i++)
	{
		connfd = p->clientfd[i];
		rio = p->clientrio[i];
		if ((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0)
		{
			byte_cnt +=n;
			printf("Server received %d (%d total) bytes on fd %d\n",
					n, byte_cnt, connfd);
			Rio_writen(connfd, buf, n);
		}
		else
		{
			Close(connfd);
			FD_CLR(connfd, &p->read_set);
			p->clientfd[i] = -1;
		}
	}
}

注:客户端只有交替输入时,才可以正确处理


6,基于多线程,实现了多个客户端的echo服务器
/*
 * echoserveri.c - An iterative echo server
 */
/* $begin echoserverimain */
#include "csapp.h"

void Pthread_create(pthread_t *tidp, pthread_attr_t *attrp,
		    void * (*routine)(void *), void *argp)
{
    int rc;

    if ((rc = pthread_create(tidp, attrp, routine, argp)) != 0)
	posix_error(rc, "Pthread_create error");
}

//常常配合pthread_self,来终止当前线程
void Pthread_cancel(pthread_t tid)
{
    int rc;

    if ((rc = pthread_cancel(tid)) != 0)
        posix_error(rc, "Pthread_cancel error");
}

//阻塞, 直到等到指定的线程终止。
void Pthread_join(pthread_t tid, void **thread_return)
{
    int rc;

    if ((rc = pthread_join(tid, thread_return)) != 0)
        posix_error(rc, "Pthread_join error");
}

//线程默认是可结合的:意味着可以被其他线程收回资源和杀死
//该函数使线程变为分离的,那么 其资源就不必显式的回收了。
void Pthread_detach(pthread_t tid)
{
    int rc;
    if ((rc = pthread_detach(tid)) != 0)
        posix_error(rc, "Pthread_detach error");
}

//线程显示终止
//主线程调用,等待所有其他线程终止,然后终止主线程和整个进程
void Pthread_exit(void *retval)
{
    pthread_exit(retval);
}
//某个 线程调用exit,会终止进程以及所有与该进程相关的线程

//返回自己线程的ID
pthread_t Pthread_self(void)
{
    return pthread_self();
}

void Pthread_once(pthread_once_t *once_control, void (*init_function)()) {
    pthread_once(once_control, init_function);
}


void echo(int connfd);
void* thread(void* vargp);

int main(int argc, char **argv)
{
	int listenfd, *connfdp, port, clientlen = sizeof(struct sockaddr_in);
    struct sockaddr_in clientaddr;
	pthread_t tid;
    if (argc != 2)
	{
		fprintf(stderr, "usage: %s <port>\n", argv[0]);
		exit(0);
    }
    port = atoi(argv[1]);
    listenfd = Open_listenfd(port);

    while (1)
	{
	    connfdp = (int*)Malloc(sizeof(int));
		*connfdp =Accept(listenfd, (SA *)&clientaddr, &clientlen);
		Pthread_create(&tid, NULL, thread, connfdp);
    }
    exit(0);
}

void* thread(void* vargp)
{
	int connfd = *((int*)vargp);
	printf("server connected to a client.\n");   
	Free(vargp);
	echo(connfd);
	Close(connfd);
	return NULL;
}
分享到:
评论

相关推荐

    Unix Echo服务器和客户端(C语言)

    有两个版本(C语言,内有完整的说明文档): 1.实现客户端发一句,服务器返回一个 2.客户端一次连接可以发送多句(每句就是一个package),服务器返回最初的5句(不足5句,就返回所有的)

    echo服务器和客户端程序

    echo客户程序,即通过命令行输入任何字符串,等回车后发送给服务器。发送的部分要包括换行符。然后等待服务器响应。 服务端程序,收到换行符后将收到的字符串原样返回给客户。

    Echo.Net:C#远程控制(服务端、客户端)

    C#远程控制(服务端、客户端) 主要功能有屏幕监控、鼠标键盘控制、任务管理器、Telnet、系统信息查看、关机注销重启等 主要用于学习并熟悉C#... 熟悉了XML的序列化、反序列化,Socket通讯,Win32API调用等

    C# Winform做的TCP ECHO服务端

    C# Winform做的TCP ECHO服务端。 功能:接收客户端发来的信息然后返回给客户端。 代码内有简单注释说明。

    C#winform做的TCP ECHO服务端

    C#winform做的TCP ECHO服务端。 功能:与客户端建立TCP连接,接收客户端发来的数据,然后回发给客户端。

    linux echo服务客户端与服务器端程序

    linux echo服务客户端与服务器端程序

    一个简单的完成端口(服务端/客户端)类

    源码使用了高级的完成端口(IOCP)技术,该技术可以有效地服务于多客户端。本文提出了一些IOCP编程中出现的实际问题的解决方法,并提供了一个简单的echo版本的可以传输文件的客户端/服务器程序。

    Python 中的 Socket 编程

    运行echo 程序的客户端和服务端 查看 socket 状态 通信流程的分解 处理多个连接 多连接的客户端 / 服务器程序 多连接的服务端 多连接的客户端 运行多连接的客户端和服务端程序 客户端 / 服务器应用程序 应用的协议头...

    一个简单的完成端口(服务端-客户端)类

    本文的源码使用了高级的完成端口(IOCP)技术,该技术可以有效地服务于多客户端。本文提出了一些 IOCP 编程中出现的实际问题的解 ...决方法,并提供了一个简单的 echo 版本的可以传输文件的客户端/服务器程序。

    TCP套接字的服务器与客户端

    源码及实验报告 1.基于流式套接字的时间同步服务器设计 2.基于流式套接字的服务器回射程序设计 (1)客户接收一行数据 (2)服务器定长接收数据 (3)客户服务器变长接收数据 3.实现并发服务器。...

    基于JavaScript的小超市收银管理系统源码(前台和后台).zip

    服务端和客户端打包在一起,正常打包使用时软件打开客户端后会在后台静默启动服务端。 当局域网内有多台收银机时,可由其中一台充当服务端,其他收银机充当客户端。 在软件根目录下建立一个.noserver的空文件即可...

    echoServer.md

    一种简单的回显服务器搭建,里面包含服务端和客户端编程,非常适用于新手学习。本人在centos7.0版本上成功测试,同样适用于其他Linux发行版

    ios-UDPdemo1.1.zip

    第一步:假设设备A为服务端,则设备A输入端口号和昵称,ip会自动获取本机的ip,点击监听,此时下面会出现“udp echo server started on port 你输入的端口号”。这就表明服务端正在监听。 第二步:设备B自然就是...

    用PHP的socket实现客户端到服务端的通信实例详解

    一、server.php服务端: &lt;?...error_reporting(E_ALL); set_time_limit(0); ob_implicit_flush();...if (($sock = socket_create(AF... echo "socket创建失败原因 " . socket_strerror($sock) . "\n"; } if (($ret = so

    netty入门Demo源码

    Netty服务端与客户端,数据的发送与接收 使用:先运行TimeServer,在运行TimeClient。成功连接后,服务器发送一个时间给客户端。输出到客户端控制台 4.第三个示例 com.user_1 Netty将java对象作为数据的发送与...

    MQ服务消息队列介绍

    本节以上述1,2两个步骤安装好的MQ服务端和MQ客户端为例,介绍如何配置MQ的服务端和客户端使两者能够互联。 (1)服务端配置 1)点"开始"-&gt;"所有程序"-&gt;"IBM WebSphere MQ"-&gt;"WebSphere MQ 资源管理器",进入...

    go语言聊天程序

    用go语言实现的聊天程序 两套 1对1 服务器与客户端 Clock服务端程序 Echo客户端程序 多对多 服务器做中转 server服务端程序 client客户端程序

    VC6.0 网络程序设计 UDP 文件传输 客户端与服务器端

    VC6.0 网络程序设计 UDP echo 结构体 传送指针 客户端与服务器端

    网络编程学习文件

    网络编程的简单实例,time服务,daytime服务,echo服务,服务端客户端

    免费的VOIP网络电话,Android平台SIP客户端

    免费的VOIP网络电话,Android平台SIP客户端 支持服务端: Cisco CallManager, OpenSER, Kamailio, OpenSIPS, Asterisk, Radvision, Nortel, Avaya等等 支持语音编码: G.711 aLaw/uLaw, G.722.1, G.722, SPEEX, SPEEX...

Global site tag (gtag.js) - Google Analytics