`
kmplayer
  • 浏览: 508868 次
  • 性别: 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语言实现的Unix Echo服务器和客户端系统。这个系统由两个主要部分组成:Echo服务器和Echo客户端,它们通过TCP套接字进行通信。下面我们将详细讨论相关知识点。 首先,我们要了解**TCP...

    服务端和客户端的程序,思维导图

    在IT行业中,服务端和客户端程序是构建网络应用程序的基础架构,它们之间进行通信以实现数据交换和服务提供。这里,我们主要关注的是C++语言在服务端和客户端编程中的应用,以及通过思维导图来理解这一过程。 首先...

    echo服务器和客户端程序

    在IT领域,网络通信是核心部分之一,而“echo服务器和客户端程序”是学习网络编程时常见的基础示例。这个概念通常用于展示TCP或UDP协议的基本工作原理,以及如何实现客户端与服务器之间的数据交换。 首先,我们要...

    TCP服务端客户端交互

    本实验“TCP服务端客户端交互”旨在通过实践来理解TCP服务端和客户端如何进行数据通信,以及如何实现回射(echo)功能,即服务端接收到客户端发送的数据后,原样返回给客户端。 首先,TCP是一种面向连接的、可靠的...

    php接口api从服务端到客户端教程[参照].pdf

    3. 返回响应:服务端最后将处理后的数据通过`echo`返回给客户端。 此示例中的API设计相当简单,只包含了基本的身份验证和数据返回。在实际应用中,API通常会更复杂,包括错误处理、数据验证、授权机制、数据加密、...

    PHP SOAP服务端客户端实例

    下面我们将详细讲解服务端和客户端的实现过程。 首先,让我们关注服务端的实现。在提供的压缩包中,`server.php`文件就是服务端的示例。服务端的主要任务是定义一个或多个函数,这些函数将暴露给SOAP客户端调用。在...

    基于Java的Tcp服务端与客户端的JAVA实例源代码.zip

    通过提供的"基于Java的Tcp服务端与客户端的JAVA实例源代码.zip"文件,我们可以深入理解TCP通信的基本原理和实现方式。 首先,让我们分析TCP服务端的实现。服务端的核心是`ServerSocket`类,它用于监听客户端的连接...

    C# Winform做的TCP ECHO服务端

    本项目是一个使用C#编程语言和Winform框架实现的TCP ECHO服务端,其主要功能是接收客户端发送的信息并原样返回给客户端,这有助于开发者测试网络连接的可靠性以及数据传输的正确性。 首先,我们要理解TCP ECHO...

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

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

    基于java的Tcp服务端与客户端的JAVA实例源代码.zip

    3. `InputStream`和`OutputStream`:客户端和服务端通过它们进行数据的读写。 4. `BufferedReader`和`PrintWriter`:用于更方便地读写字符串数据。 在实际应用中,我们可能需要处理多个并发连接,这就需要多线程...

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

    在本项目“一个简单的完成端口(服务端/客户端)类”中,作者提供了使用IOCP技术实现的服务器和客户端程序,用于文件传输。下面将详细介绍这个系统的关键知识点。 1. **完成端口(IOCP)概念**: - IOCP是一种高效...

    C#winform做的TCP ECHO服务端

    本项目“C# WinForm做的TCP ECHO服务端”正是基于这样的背景创建的,旨在为开发者提供一个基础的TCP服务器示例,它能够接收客户端发送的数据并原样返回,即“回显”功能。 首先,我们来深入理解TCP ECHO服务端的...

    Python 中的 Socket 编程

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

    php客户端服务端demo

    本示例"php客户端服务端demo"展示了如何利用PHP实现客户端和服务端之间的数据交互,重点在于使用cURL库来封装HTTP协议,进行POST数据传输,并且包含身份验证和IP限制功能。 首先,我们来看客户端部分。cURL是PHP的...

    Socket客户端和服务端的编程实现(C和android附运行截图).docx

    ### Socket客户端和服务端的编程实现(C和Android) #### 实验目的及要求 - **理解套接字(Socket)通信的基本原理和机制**:了解Socket的工作方式及其在网络通信中的作用。 - **理解进程间通信的机制**:熟悉不同进程...

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

    本实验旨在通过实际操作,掌握TCP套接字的基本操作,包括服务器端的建立、监听、接受连接以及客户端的连接、发送和接收数据。以下是实验的主要知识点: 1. **TCP套接字的基础概念**: TCP(Transmission Control ...

    华为高斯(Gauss)gsql客户端安装包及配置说明

    本安装包提供了OpenGauss的客户端工具——gsql,它允许用户在不安装完整数据库服务的情况下,通过远程连接对高斯数据库进行操作和管理。 首先,我们要理解gsql客户端的作用。gsql是一个基于命令行的交互式查询工具...

    SignalR-实现web浏览器客户端与服务端的推送功能

    在 `PersistentConnection` 内部,定义了五个主要的事件处理器:`OnConnected`、`OnReconnected`、`OnReceived`、`OnError` 和 `OnDisconnect`,这些事件处理器帮助处理客户端和服务端之间的交互逻辑。 2. **Hub**...

    安卓websocket(客服端和服务端写在app端) 案例

    在这个案例中,我们将探讨如何在安卓应用中同时实现WebSocket客户端和服务端的功能。 1. **WebSocket API简介** WebSocket API是HTML5的一部分,为Web应用程序提供了低延迟、双向通信的能力。与HTTP不同,...

Global site tag (gtag.js) - Google Analytics