小红在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
#include
#include
#include
#include
#include
#include
//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;
}