多线程版本的网络套接字编程

哈喽!今天讲的主题利用tcp是网络套接字编程,简易服务器与客户端之间的通信:

 

首先我们了解联想一下我们聊QQ的场景,此时假若小红和小明在聊天,这个过程是什么呢?

小红在QQ聊天框写下自己说的话,此时QQ客户端程序将小红说的话发送给QQ服务器(此时牵扯到信息的发送流程,牵扯到的网络知识,暂时不做讨论),QQ服务器接收到消息,查看消息的头部信息,知道想要发送给谁,此时QQ服务器将消息发送给小明,小明收到消息,编辑信息并且发送给QQ服务端,QQ服务端将信息再发送给小红...

我们要注意一点:客户端和客户端不能直接进行通信,需要服务器作为中间人进行调节。

我们的客户端只需要与服务端建立连接,并且与服务端实现收发数据的功能。

我们的服务端需要与每一个客户端建立连接,并且实现收发数据的功能,因为需要对每一个客户端进行链接并且收发数据,可以利用:多进程(https://blog.csdn.net/xinger_28/article/details/94211219)或者多线程(本文)进行实现。

接下来讲解流程:

1.服务端一定要先启动,创建套接字。在美和中创建socket结构体来描述套接字,返回套接字句柄便于用户操作,并且使进程与网卡之间建立联系。

            int(套接字句柄) socket(int domain(地址域名), int type(套接字类型), int protocol(协议选择)); 

2.服务端需要为套接字绑定地址信息, 告诉操作系统,这个服务器的端口和地址是哪一个。

       int bind(int sockfd(套接字句柄), const struct sockaddr *addr(套接字地址结构体), socklen_t addrlen(结构体长度));

        为什么套接字地址结构体需要用 struct sockaddr这个来描述,是因为现在的地址结构体有两种,一种是sockaddr_in和sockaddr_un这两种,为了统一接口,使用sockaddr,并且利用socklen_t来告诉操作系统需要怎么解析这个结构体。

                                                    

3.服务端开始监听,告诉操作系统可以开始接收客户端的请求了,若请求来了,顺便完成连接

        int listen(int sockfd(套接字描述符--监听描述符), int backlog(最大并发连接数)); 

        我们的sockfd从建立连接到现在一步步填充信息,此时是最能描述服务端的描述符句柄,因此我们利用它来接收客户端连接请求。,请注意最大并发连接数,是指此时服务端所能连接最多的客户端数量,这个是由我们程序员的设定数量限制,此时我们需要理解的是在这个服务端存在两个队列,一个是未完成socket连接队列,一个是已完成连接队列,如果此时已完成连接队列已满,接下来客户端的连接请求将会被丢掉,连接失败。

4.客户端此时已经经历过了:创建套接字,为套接字绑定地址信息(这个操作不建议,因为我们现在使用网络是用DHCP和NAT技术,操作系统将自动为我们选择合适的地址信息)。

     接下来客户端发起连接请求:如果客户端自己没有绑定地址信息,操作系统将会在连接之前为我们绑定地址信息,此时利用sockfd向服务端发起连接,因此需要知道服务端的地址信息。

int(返回值将会用来进行收发数据) connect(int sockfd(客户端套接字描述符), const struct sockaddr *addr(服务端地址信息), socklen_t addrlen(地址信息长度));

5.服务端监听到有客户端请求连接,查看当前是否可以继续连接,如果可以连接,将会接受客户端的连接。

int(用于收发数据的描述符) accept(int sockfd(监听描述符), struct sockaddr *addr(客户端的地址信息), socklen_t *addrlen(地址信息长度));

这个函数时阻塞的,如果没有客户端到达,将会一直等待直到有客户端到来,如果有客户端到来,操作系统将会新建一个套接字描述符,这个描述符专门用来和这个客户端进行收发数据,并且在客户端地址信息中填充客户端的地址信息,方便程序员用来查看当前请求连接客户端的信息。

6.此时客户端服务端已经完全建立好连接,只需要进行收发数据

ssize_t recv(int sockfd(通信描述符), void *buf(缓冲区), size_t len(最大可以接受数据的长度), int flags(0,阻塞接收));

ssize_t send(int sockfd(通信描述符), const void *buf(缓冲区), size_t len(发送的数据长度), int flags(发送方式));

需要注意返回值:<0 出错  recv :==0 连接已断开      >0 接受的长度       send:  ==0 触发异常退出    >0  实际发送数据长度

7.关闭套接字:断开连接

int close(int fd(套接字描述符/监听套接字))

代码

//tcpsocket.h

#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<pthread.h>
//using namespace std;
//
#define CHEECK_RET(q) if(q<0){return q;}
int Socket();
int Bind(int sockfd,const char*ip,uint16_t port);
int Listen(int sockfd);
int Connect(int sockfd,const char * ip,uint16_t port);
int Accept(int sockfd,char**ip,uint16_t*port);
int Recv(int newfd,char*buf,size_t len);
int Send(int newfd,char*buf,size_t len);
int Close(int fd);
//tcpsocket.c

#include"tcpsocket.h"
int Socket()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	return sockfd;
}

int Bind(int sockfd,const char*ip,uint16_t port)
{
	struct sockaddr_in addr;
	addr.sin_family=AF_INET;
	addr.sin_port=htons(port);
	addr.sin_addr.s_addr=inet_addr(ip);
	int len=sizeof(struct sockaddr_in);
	int ret=bind(sockfd,(struct sockaddr*)&addr,len);
	return ret;
}

int Listen(int sockfd)
{
	int ret=listen(sockfd,10);
	return ret;
}

int Connect(int sockfd,const char* ip,uint16_t  port)
{
	struct sockaddr_in addr;
	socklen_t len=sizeof(struct sockaddr_in);
	addr.sin_family=AF_INET;
	addr.sin_port=htons(port);
	addr.sin_addr.s_addr=inet_addr(ip);
	int ret=connect(sockfd,(struct sockaddr*)&addr,len);
	if(ret<0)
	{printf("connect error\n\n"); return -1;}
	return ret;
}

int Accept(int sockfd,char**ip,uint16_t * port)
{
	struct sockaddr_in clic ;
	int len=sizeof(struct sockaddr_in);
	int newfd=accept(sockfd,(struct sockaddr*)&clic,&len);
	if(newfd<0)
	{
		return newfd;}
	*ip=inet_ntoa(clic.sin_addr);
	*port=ntohs(clic.sin_port);
	return newfd;
}

int Recv(int newfd,char*buf,size_t len)
{
	int ret=recv(newfd,buf,len,0);
	if(ret<0)
	printf("recv error\n");
	else if (ret==0)
	printf("connection shoutdown\n");
	return ret;
}

int Send(int newfd,char * buf,size_t len)
{
	int ret=send(newfd,buf,len,0);
	if(ret<0)
	printf("send error\n");
	else if (ret==0)
	printf("connection shoutdown\n");
	return ret;
}
int  Close(int fd)
{
	close(fd);
}
//thread_service.c
#include"tcpsocket.h"

int  cheakRet(char*info,int ret)
{
	if(ret<0)

	{printf("%s\n",info);return -1;}
	return ret;
}

int Recv_Send(int fd,char*clicip,uint16_t clicport)
{
	while(1){
	char buf[1024]={0};
	int ret=Recv(fd,buf,1023);	
	printf("connect : ip: %s  port: %d\n",clicip,clicport);
	printf("*   clic say:  %s \n",buf);
	if(ret<0)
	{printf("recv error\n");return -1;}
	else if(ret==0)
	{printf("connect is error\n");  
		return ret;}
	memset(buf,0x00,1024);
	printf("* send to :");
	scanf("%s",buf);
	printf("\n");
	ret=Send(fd,buf,strlen(buf));
	}
	return 0;
}
	
void Accept_(int sockfd,char*clicip,uint16_t clicport)
{
	pid_t ret=fork();
	if(ret==0)
	{
	   pid_t gh=fork();
	    if(gh==0)
		Recv_Send(sockfd,clicip,clicport);
	   exit(0);
	}
	else if(ret>0)
	{close(sockfd);
	wait(ret);
	}
	else
	{printf("fork() error\n");
	}
}

int main()
{
	int sockfd=Socket();
	cheakRet("Socket error",sockfd);
	int ret=Bind(sockfd,"192.168.136.132",10000);
	cheakRet("Bind error",ret);
	ret=Listen(sockfd);
	cheakRet("Listen error",ret);
	while(1)
	{
	struct sockaddr_in clicaddr;
        socklen_t len=sizeof(struct sockaddr_in);
        char*clicip;
        uint16_t clicport;
        int newfd=Accept(sockfd,&clicip,&clicport);
	printf("connect : ip: %s  port: %d\n",clicip,clicport);
	Accept_(newfd,clicip,clicport);
	}	
	Close(sockfd);
	return 0;
}
//clic.c
#include"tcpsocket.h"

int  cheakRet(char*info,int ret)
{
	if(ret<0)
	{printf("%s\n",info);return -1;}
	return ret;
}

void*  Recv_Send(void *se)
{
	int fd=(int)se;
	while(1){
	char buf[1024]={0};
	int ret=Recv(fd,buf,1023);	
	printf("*   clic say:  %s \n",buf);
	if(ret<0)
	{printf("recv error\n");
	}
	else if(ret==0)
	{printf("connect is error\n");  
		}
	memset(buf,0x00,1024);
	printf("* send to :");
	scanf("%s",buf);
	printf("\n");
	ret=Send(fd,buf,strlen(buf));
	}
}
	

int main()
{
	int sockfd=Socket();
	cheakRet("Socket error",sockfd);
	int ret=Bind(sockfd,"192.168.136.132",10000);
	cheakRet("Bind error",ret);
	ret=Listen(sockfd);
	cheakRet("Listen error",ret);
	while(1)
	{
	struct sockaddr_in clicaddr;
        socklen_t len=sizeof(struct sockaddr_in);
        char*clicip;
        uint16_t clicport;
        int newfd=Accept(sockfd,&clicip,&clicport);
	printf("connect : ip: %s  port: %d\n",clicip,clicport);
	pthread_t pid=0;
	ret=pthread_create(&pid,NULL,Recv_Send,(void *)newfd);
	if(ret>0)
	{printf("pthread_create error\n");
	continue;}
	pthread_detach(pid);
	}	
	Close(sockfd);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值