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;
}
分享到:
相关推荐
有两个版本(C语言,内有完整的说明文档): 1.实现客户端发一句,服务器返回一个 2.客户端一次连接可以发送多句(每句就是一个package),服务器返回最初的5句(不足5句,就返回所有的)
echo客户程序,即通过命令行输入任何字符串,等回车后发送给服务器。发送的部分要包括换行符。然后等待服务器响应。 服务端程序,收到换行符后将收到的字符串原样返回给客户。
C#远程控制(服务端、客户端) 主要功能有屏幕监控、鼠标键盘控制、任务管理器、Telnet、系统信息查看、关机注销重启等 主要用于学习并熟悉C#... 熟悉了XML的序列化、反序列化,Socket通讯,Win32API调用等
C# Winform做的TCP ECHO服务端。 功能:接收客户端发来的信息然后返回给客户端。 代码内有简单注释说明。
C#winform做的TCP ECHO服务端。 功能:与客户端建立TCP连接,接收客户端发来的数据,然后回发给客户端。
linux echo服务客户端与服务器端程序
源码使用了高级的完成端口(IOCP)技术,该技术可以有效地服务于多客户端。本文提出了一些IOCP编程中出现的实际问题的解决方法,并提供了一个简单的echo版本的可以传输文件的客户端/服务器程序。
运行echo 程序的客户端和服务端 查看 socket 状态 通信流程的分解 处理多个连接 多连接的客户端 / 服务器程序 多连接的服务端 多连接的客户端 运行多连接的客户端和服务端程序 客户端 / 服务器应用程序 应用的协议头...
本文的源码使用了高级的完成端口(IOCP)技术,该技术可以有效地服务于多客户端。本文提出了一些 IOCP 编程中出现的实际问题的解 ...决方法,并提供了一个简单的 echo 版本的可以传输文件的客户端/服务器程序。
源码及实验报告 1.基于流式套接字的时间同步服务器设计 2.基于流式套接字的服务器回射程序设计 (1)客户接收一行数据 (2)服务器定长接收数据 (3)客户服务器变长接收数据 3.实现并发服务器。...
服务端和客户端打包在一起,正常打包使用时软件打开客户端后会在后台静默启动服务端。 当局域网内有多台收银机时,可由其中一台充当服务端,其他收银机充当客户端。 在软件根目录下建立一个.noserver的空文件即可...
一种简单的回显服务器搭建,里面包含服务端和客户端编程,非常适用于新手学习。本人在centos7.0版本上成功测试,同样适用于其他Linux发行版
第一步:假设设备A为服务端,则设备A输入端口号和昵称,ip会自动获取本机的ip,点击监听,此时下面会出现“udp echo server started on port 你输入的端口号”。这就表明服务端正在监听。 第二步:设备B自然就是...
一、server.php服务端: <?...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服务端与客户端,数据的发送与接收 使用:先运行TimeServer,在运行TimeClient。成功连接后,服务器发送一个时间给客户端。输出到客户端控制台 4.第三个示例 com.user_1 Netty将java对象作为数据的发送与...
本节以上述1,2两个步骤安装好的MQ服务端和MQ客户端为例,介绍如何配置MQ的服务端和客户端使两者能够互联。 (1)服务端配置 1)点"开始"->"所有程序"->"IBM WebSphere MQ"->"WebSphere MQ 资源管理器",进入...
用go语言实现的聊天程序 两套 1对1 服务器与客户端 Clock服务端程序 Echo客户端程序 多对多 服务器做中转 server服务端程序 client客户端程序
VC6.0 网络程序设计 UDP echo 结构体 传送指针 客户端与服务器端
网络编程的简单实例,time服务,daytime服务,echo服务,服务端客户端
免费的VOIP网络电话,Android平台SIP客户端 支持服务端: Cisco CallManager, OpenSER, Kamailio, OpenSIPS, Asterisk, Radvision, Nortel, Avaya等等 支持语音编码: G.711 aLaw/uLaw, G.722.1, G.722, SPEEX, SPEEX...