实现多进程&多线程的并发服务器(socket编程)

1、多进程server.c

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include<ctype.h>
#include <sys/types.h>
#include <sys/wait.h>

//回收子进程
void wait_child(int signo)
{
while(waitpid(0,NULL,WNOHANG)>0);
return ;
}   


int main()
{

	//1、创建套接字
	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	//2、绑定套接字
	struct sockaddr_in serveraddr;
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr("192.168.3.65");
	serveraddr.sin_port = htons(8888);

	bind(listenfd,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr));

	//3、监听套接字
	listen(listenfd,10);//之后的所有客户端连接都存储在监听队列中

	socklen_t len;
	struct sockaddr_in client;

    pid_t pid;
    char buf[50];
    int num;
    int clientfd;

    char clie_ip[100];
    while(2){
	//循环取出监听队列的链接套接字
	clientfd = accept(listenfd,(struct sockaddr *)&client,&len);
	//打印其他客户端的连接信息
	printf("client ip:%s  client port:%d \n",inet_ntop(AF_INET,&client.sin_addr.s_addr,clie_ip,sizeof(clie_ip)),ntohs(client.sin_port));
    pid = fork();
    if(pid<0){
			perror(" pid error\n");
			exit(1);
		}
    if(pid==0)
        {
            close(listenfd);
            break;
        }
    else{
			close(clientfd);
			signal(SIGCHLD,wait_child);
			}
			}
    if(pid==0){
		while(1)
		{
			bzero(buf,50);
			num = read(clientfd,buf,sizeof(buf));
			if(num==0){
			   // close(clientfd);
				return 0;
			}else if(num == -1){
					perror("read error");
					exit(1);
			}
			else{
				for(int i=0;i<num;i++)
					buf[i] = toupper(buf[i]);
					write(clientfd,buf,num);
					write(STDOUT_FILENO,buf,num);
			}
		}
	 }
    
	return 0;
}

2、多线程server.c

#include <sys/types.h>          /* See NOTES */
#include <signal.h>
       #include <sys/socket.h>
       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <netinet/ip.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

//线程处理函数
void * connect_hander(void * arg)
{
	
	int clientfd = *(int *)arg;//得到通信的套接字
	char buf[50];
	//----通信
	while(1)
	{
		bzero(buf,50);	
		read(clientfd,buf,50);
		printf("%s",buf);
		write(clientfd,buf,50);
		if(strcmp(buf,"close\n") == 0)
		{
			close(clientfd);
			pthread_exit(NULL);
		}
	}
}

int main()
{

	//1、创建套接字
	int listenfd = socket(AF_INET,SOCK_STREAM,0);
	//2、绑定套接字
	struct sockaddr_in serveraddr;
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr("192.168.3.65");
	serveraddr.sin_port = htons(8888);

	bind(listenfd,(struct sockaddr *)&serveraddr,sizeof(struct sockaddr));

	//3、监听套接字
	listen(listenfd,10);//之后的所有客户端连接都存储在监听队列中
	
	//循环取出监听队列的链接套接字,如果有连接就创建一个线程进行通信,猪线程继续循环监听
	int clientfd = -1;
	while(1)
	{
		struct sockaddr_in client;
		bzero(&client,sizeof(struct sockaddr));
		socklen_t len;
		clientfd = accept(listenfd,(struct sockaddr *)&client,&len);
        printf("accept ok\n");

		//创建线程
		pthread_t tid;	

		pthread_create(&tid,NULL,connect_hander,&clientfd);

		//主线程继续检测连接,进行下次循环accept
		
	}
	
	close(listenfd);

	return 0;
}

测试代码,可以打开多个进程测试

nc 192.168.3.163 8888

3、客户端client.c

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    
    struct sockaddr_in seraddr,local;
   int lfd = socket(AF_INET,SOCK_STREAM,0);
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(8888);
    seraddr.sin_addr.s_addr = inet_addr("192.168.3.65");

    local.sin_family= AF_INET;
    local.sin_port = ntohs(9999);
    local.sin_addr.s_addr = inet_addr("192.168.3.65");
  
    bind(lfd,(struct sockaddr*)&local,sizeof(struct sockaddr));
    
   int ret = connect(lfd,(struct sockaddr*)&seraddr,sizeof(struct sockaddr));
    if(ret<0)
    {
        perror("connect error");
        return -1;
    }
    printf("connect ok\n");
    char buf[128];
    while(1){
        fgets(buf,sizeof(buf),stdin);
        write(lfd,buf,128);
        bzero(buf,128);
        read(lfd,buf,sizeof(buf));
        printf("%s\n",buf);

    }



    return 0;
}

随记:

端口复用实现(不需要再等2msl时间就可以重开服务器端口)

int opt =1;
setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
 

主机字节序和网络字节序
        端口号发送时htons
        端口号接收时ntohs
        ip地址inet_addr()
发生强转的几个
        bind    accept(第三个参数是地址,客户端)   connect

记录连接的ip地址和端口号
        char clieip[100];
        printf("client ip:%s  client port:%d         \n",inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,cline_ip,sizeof(clie_ip)),
        ntohs(clieaddr.sin_port));

多进程并发:
        当子进程退出时,父进程无法去回收子进程,此时需要调用waitpid()回调机制
        使用信号捕捉SIGCHLD()进行回收,内核回收效率更高
        signal(SIGCHLD,wait_child);
        然后写wait_child函数
void wait_child(int signo)
{
    while(waitpid(0,NULL,WNOHANG)>0);
    return ;
}
查看进程
        netstat -apn | grep 8888

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值