TCP服务器建立流程:
1.建立socket套接字。
2.绑定IP地址与端口号。
3.建立监听队列,让套接字进入到被动监听状态。
4.接受连接,产生新的套接字。
5.设计数据的收发。
父进程:负责建立链接
子进程:负责与客户端的数据交互。
ps:图片来自华清远见
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#define BACKLOG 10
void sig_handler(int signum)
{
pid_t pid;
pid=wait(NULL);
printf("child process is recycled successful,pid=%d\n",pid);
}
void recv_send(int cfd)
{
char buf[128] = {0};
ssize_t recv_bytes, send_bytes;
while(1)
{
memset(buf,0,sizeof(buf));
//接收客户端的信息储存到buf中
recv_bytes = recv(cfd, buf, sizeof(buf),0);
if(recv_bytes == -1)
{
perror("recv");
break;
//客户端退出返回0,服务器这里也退出循环
}else if(recv_bytes == 0)
{
printf("client shutdown\n");
break;
}
printf("buf: %s\n",buf);
//将收到的数据原样返回给发送的客户端
send_bytes = send(cfd, buf, recv_bytes, 0);
if(send_bytes == -1)
{
perror("send");
break;
}
}
//用完了就关掉
colse(cfd);
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
int ret;
pid_t pid;
int sfd,cfd;
struct sockaddr_in src,cli;
socklen_t len = sizeof(src);
socklen_t addrlen = sizeof(cli);
if(signal(SIGCHLD,sig_handler)==SIG_ERR)
{
perror("");
return -1;
}
//创建套接字,第二个参数为流式套接字类型
sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1)
{
perror("socket fail");
return -1;
}
//服务器地址结构体填充,地址族,IP PORT
src.sin_family = AF_INET;
src.sin_port = htons(atoi(argv[2]));
src.sin_addr.s_addr = inet_addr(argv[1]);
//绑定套接字,服务器ip和port口号
ret = bind(sfd, (const struct sockaddr *)&src, len );
if(ret == -1)
{
perror("bind");
return -1;
}
//将套接字设定为被动监听状态,监听客户端的连接请求,10为未决队列长度
ret = listen(sfd, BACKLOG);
if(ret == -1)
{
perror("listen");
return -1;
}
while(1)
{
//接收客户端连接请求,产生连接套接字,用于和客户端通信,通过cli保存客户端ip地址和port口号
cfd = accept(sfd, (struct sockaddr *)&cli,&addrlen);
if(cfd == -1)
{
perror("accept");
return -1;
}
printf("client IP: %s PORT: %d\n", inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));//打印客户端IP 和 PORT ,需要将网络字节序转换为主机字节序
pid = fork();
if(pid < 0)
{
perror("fork");
return -1;
}else if(pid == 0)
{//子进程不需要用到监听套接字,可以关掉
close(sfd);
recv_send(cfd);
}else if(pid>0)
{
//父进程不需要连接套接字,可以关掉
close(cfd);
}
}
return 0;
}
主线程:负责建立连接。
子线程:负责与客户端的数据交互。
ps:图片来自华清远见
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BACKLOG 10
void *recv_send(void *arg)
{
int cfd=*(int *)arg;
char buf[128] = {0};
ssize_t recv_bytes, send_bytes;
while(1)
{
memset(buf,0,sizeof(buf));
//接收客户端的信息储存到buf中
recv_bytes = recv(cfd, buf, sizeof(buf),0);
if(recv_bytes == -1)
{
perror("recv");
break;
//客户端退出返回0,服务器这里也退出循环
}else if(recv_bytes == 0)
{
printf("client shutdown\n");
break;
}
printf("buf: %s\n",buf);
//将收到的数据原样返回给发送的客户端
send_bytes = send(cfd, buf, recv_bytes, 0);
if(send_bytes == -1)
{
perror("send");
break;
}
}
//用完了就关掉
pthread_exit(NULL);
}
int main(int argc, char **argv)
{
pthread_t tid;
int ret;
int sfd,cfd;
struct sockaddr_in src,cli;
socklen_t len = sizeof(src);
socklen_t addrlen = sizeof(cli);
//创建套接字,第二个参数为流式套接字类型
sfd = socket(AF_INET, SOCK_STREAM, 0);
if(sfd == -1)
{
perror("socket fail");
return -1;
}
//服务器地址结构体填充,地址族,IP PORT
src.sin_family = AF_INET;
src.sin_port = htons(atoi(argv[2]));
src.sin_addr.s_addr = inet_addr(argv[1]);
//绑定套接字,服务器ip和port口号
ret = bind(sfd, (const struct sockaddr *)&src, len );
if(ret == -1)
{
perror("bind");
return -1;
}
//将套接字设定为被动监听状态,监听客户端的连接请求,10为未决队列长度
ret = listen(sfd, BACKLOG);
if(ret == -1)
{
perror("listen");
return -1;
}
while(1)
{
//接收客户端连接请求,产生连接套接字,用于和客户端通信,通过cli保存客户端ip地址和port口号
cfd = accept(sfd, (struct sockaddr *)&cli,&addrlen);
if(cfd == -1)
{
perror("accept");
return -1;
}
printf("client IP: %s PORT: %d\n", inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));//打印客户端IP 和 PORT ,需要将网络字节序转换为主机字节序
//创建子线程,负责和客户端交互
ret=pthread_create(&tid,NULL,recv_send,(void *)&cfd);
if(ret!=0)
{
errno=ret;
perror("tid wu");
return -1;
}
pthread_detach(tid);//将线程标记分离
}
return 0;
}
TCP客户端代码
TCP客户端设计流程:
1.建立socket套接字。
2.连接服务器,发出连接请求。
3.数据读写。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int ret;
int sfd;
ssize_t recv_bytes,send_bytes;
char buf[128]={0};
struct sockaddr_in src;
socklen_t len=sizeof(src);
if(argc<3)
{
perror("argc wu");
return -1;
}
sfd=socket(AF_INET,SOCK_STREAM,0);//流式套接字
if(sfd==-1)
{
perror("socket wu");
return -1;
}
src.sin_family=AF_INET;
src.sin_port=htons(atoi(argv[2]));
src.sin_addr.s_addr = inet_addr(argv[1]);
//连接服务器发出连接请求
ret = connect(sfd,(const struct sockaddr*)&src,len);
if(ret==-1)
{
perror("connect wu");
return -1;
}
while(1)
{
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
//客户端获取屏幕终端的数据,发送数据给服务器
send_bytes = send(sfd,buf,strlen(buf),0);
if(send_bytes == -1)
{
perror("send wu");
return -1;
}
}
return 0;
}
以上是今天在华清远见学习到的关于TCP多客户端与服务器连接的代码。