Linux网络编程之循环服务器(其一)


本博客参考《Linux C/C++服务器开发实践》—— 朱文伟 李建英

什么是循环服务器?

循环服务器在同一个时刻只能响应一个客户端的请求,处理完一个客户端的工作后,才能处理下一个客户端的工作,就好像分时工作一样。循环服务器指的是对于客户端的请求和连接,服务器在处理完毕一个后,再处理另一个,即串行处理客户机的请求。这个I/O模型对应的是同步阻塞模型。
有关五种网络I/O模型的详细了解,请参考下面的博客:
链接:Linux网络编程之I/O模型(详解)

UDP循环服务器

UDP循环服务器的实现方法:UDP服务器每次从套接字上读取一个客户端的请求并处理,然后将处理结果返回给客户端。伪代码如下:

socket();
bind();
while(1){
	recvfrom();
	process();
	sendto();
}

因为UDP是无连接的,没有一个客户端可以一直占用服务器,服务器能满足每一个客户端的请求。

一个简单的UDP循环服务器代码

udpserver.cpp

#include "myhead.h"

#define PORT 10024
char rbuf[50], sbuf[100];

int main(){
    int sockfd, size, ret, val;
    char on = 1;
    struct sockaddr_in saddr;
    struct sockaddr_in raddr;

    //设置地址信息,IP信息
    size = sizeof(struct sockaddr_in);
    val = sizeof(struct sockaddr);
    memset(&saddr, 0, size);
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);

    //创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0){
        perror("create sockfd fail\n");
        return -1;
    }

    //设置端口复用
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    //绑定地址信息
    ret = bind(sockfd, (struct sockaddr*)&saddr, val);
    if(ret < 0){
        perror("bind fail\n");
        return -1;
    }

    while(1){
        puts("waitting data\n");
        ret = recvfrom(sockfd, rbuf, sizeof(rbuf), 0, (struct sockaddr*)&raddr, 
        (socklen_t*)&val);
        if(ret < 0){
            perror("recvfrom failed\n");
        }

        printf("recv data:%s\n", rbuf);
        snprintf(sbuf, sizeof(sbuf), "server has received your data(%s)\n", rbuf);
        ret = sendto(sockfd, sbuf, sizeof(rbuf), 0, (struct sockaddr*)&raddr,
        val);

        memset(rbuf, 0, sizeof(rbuf));
    }
    close(sockfd);
    getchar();
    return 0;
}

udpclient.cpp

#include <stdio.h>
#include <winsock.h>
#pragma comment(lib, "wsock32")
#define BUF_SIZE 200
#define PORT 10024
#define IP "192.168.159.128"
char wbuf[50], rbuf[100];

int main()
{
    SOCKET s;
    int len = -1;
    WSADATA wsadata;
    struct hostent* phe;
    struct servent* pse;
    struct protoent* ppe;

    struct sockaddr_in saddr, raddr;
    int fromlen, ret, type;

    if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
        printf("WSAStarup failed\n");
        return -1;
    }
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    saddr.sin_addr.s_addr = inet_addr(IP);

    if ((ppe = getprotobyname("UDP")) == 0) {
        printf("get protocol information error \n");
        WSACleanup();
        return -1;
    }

    s = socket(PF_INET, SOCK_DGRAM, ppe->p_proto);
    if (s == INVALID_SOCKET) {
        printf(" create socket error \n");
        WSACleanup();
        return -1;
    }
    fromlen = sizeof(struct sockaddr);
    printf("please inter data:");
    scanf_s("%s", wbuf, sizeof(wbuf));
    ret = sendto(s, wbuf, sizeof(wbuf), 0, (struct sockaddr*)&saddr, sizeof(struct sockaddr));

    if (ret < 0) {
        perror("sendto failed\n");
    }
    len = recvfrom(s, rbuf, sizeof(rbuf), 0, (struct sockaddr*)&raddr, &fromlen);
    if (len < 0) perror("recvfrom fail\n");
    printf("server reply:%s\n", rbuf);

    closesocket(s);
    WSACleanup();
    return 0;
}


注意,为了更贴近一线企业实战环境,客户端的程序是放到Windows上运行的,所以它的代码也使用了Windows系统提供的接口。该代码可以在Visual C++上运行,如果想深入了解Visual C++,可以参考清华大学出版社的《Viusal C++ 2017从入门到精通》一书。

TCP循环服务器

TCP服务器接受一个客户端的连接,然后处理,完成了这个客户端的所有请求后,断开连接。伪代码如下:

socket();
bind();
listen();
while(1){
	accept();
	process();
	close();
}

注:TCP循环服务器一次只能处理一个客户端的请求(除非使用一客户端一线程)。只有在这个客户端的所有请求都满足后,服务器才可以继续后面的请求。如果一个客户端占住服务器不放,则其他的客户端都不能工作,因此TCP服务器一般很少使用这个模型。
链接: 想了解更多服务器模型,可以参考这里

一个简单的TCP循环服务器代码

tcpserver.cpp

#include "myhead.h"
#define  BUF_SIZE  200
#define PORT 8888

int main()
{
	struct   sockaddr_in fsin;
	int       clisock,alen, connum = 0, len, s;
	char     buf[BUF_SIZE] = "hi,client", rbuf[BUF_SIZE];
	struct  servent  *pse;    /* server information    */
	struct  protoent *ppe;    /* proto information     */
	struct sockaddr_in sin;   /* endpoint IP address   */

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons(PORT);

	s = socket(PF_INET, SOCK_STREAM, 0);
	if (s == -1)
	{
		printf("creat socket error \n");
		getchar();
		return -1;
	}
	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
	{
		printf("socket bind error \n");
		getchar();
		return -1;
	}
	if (listen(s, 10) == -1)
	{
		printf("  socket listen error \n");
		getchar();
		return -1;
	}
	while (1)
	{
		alen = sizeof(struct sockaddr);
		puts("waiting client...");
		clisock = accept(s, (struct sockaddr *)&fsin,(socklen_t*)&alen);
		if (clisock == -1)
		{
			printf("accept failed\n");
			getchar();
			return -1;
		}
		connum++;
		printf("%d  client  comes\n", connum);
		len = recv(clisock, rbuf, sizeof(rbuf), 0);
		if (len < 0) perror("recv failed");
		sprintf(buf,"Server has received your data(%s).", rbuf);
		send(clisock, buf, strlen(buf), 0);
		close(clisock);
	}
	return 0;
}

tcpclient.cpp

#include "myhead.h"
#define  BUF_SIZE  200
#define PORT 8888
char wbuf[50], rbuf[100];
int main()
{
	char   buff[BUF_SIZE];
	SOCKET  s;
	int    len;
	WSADATA  wsadata;

	struct hostent *phe;      /*host information     */
	struct servent *pse;      /* server information  */
	struct protoent *ppe;     /*protocol information */
	struct sockaddr_in saddr;   /*endpoint IP address  */
	int   type;

	if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0)
	{
		printf("WSAStartup failed\n");
		WSACleanup();
		return -1;
	}
	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(PORT);
	saddr.sin_addr.s_addr = inet_addr("192.168.0.153");
 
	s = socket(PF_INET, SOCK_STREAM, 0);
	if (s == INVALID_SOCKET)
	{
		printf(" creat socket error \n");
		WSACleanup();
		return -1;
	}
	if (connect(s, (struct sockaddr *)&saddr, sizeof(saddr)) == SOCKET_ERROR)
	{
		printf("connect socket  error \n");
		WSACleanup();
		return -1;
	}
 
	printf("please enter data:");
	scanf_s("%s", wbuf, sizeof(wbuf));
	len = send(s, wbuf, sizeof(wbuf), 0);
	if (len < 0) perror("send failed");
	len = recv(s, rbuf, sizeof(rbuf), 0);
	if (len < 0) perror("recv failed");
	printf("server reply:%s\n", rbuf);
 
	closesocket(s);
	WSACleanup();
	return 0;
}

都读到这了,免费给个三连支持一下吧,下一期我会继续努力~

附录(myhead.h)

// myhead.h
#ifndef _MYHEAD_H
#define _MYHEAD_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/input.h>  //跟输入子系统模型有关的头文件
#include <dirent.h>
#include <stdbool.h>
#include <strings.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <poll.h>
#include <sys/epoll.h>
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值