12_TCP和UDP实现服务端和客户端的通信
一、TCP实现多进程通信
server_process.c 服务端代码:
#define _XOPEN_SOURCE
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>
void recyleChild(int arg)
{
while(1)
{
int ret=waitpid(-1,NULL,WNOHANG);
if(ret==-1)
{
//所有的子进程都被回收了;
break;
}
else if(ret==0)
{
//还有子进程活着;
break;
}
else if(ret>0)
{
//被回收了
printf("子进程%d被回收了\n",ret);
}
}
}
int main()
{
//注册信号捕捉
struct sigaction act;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
act.sa_handler=recyleChild;
sigaction(SIGCHLD,&act,NULL);
//创建socket
int lfd =socket(AF_INET,SOCK_STREAM,0);
if(lfd==-1)
{
perror("socket");
exit(-1);
}
//绑定
struct sockaddr_in saddr;
saddr.sin_addr.s_addr=0;
saddr.sin_port=htons(9999);
saddr.sin_family=AF_INET;
int ret = bind(lfd,(const struct sockaddr*)&saddr,sizeof(saddr));
if(ret==-1)
{
perror("bind");
exit(-1);
}
//监听
ret=listen(lfd,8);
if(ret==-1)
{
perror("listen");
exit(-1);
}
//不断循环,接收客户端连接;
while(1)
{
struct sockaddr_in cliaddr;
int len =sizeof(cliaddr);
//接收连接
int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
if(cfd==-1)
{
if(errno==EINTR)
{
continue;
}
perror("accept");
exit(-1);
}
//每一个连接进来就创建一个子进程进行客户端通信;
pid_t pid=fork();
if(pid==0)
{
//子进程
//获取客户端信息
char cliIP[16];
inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,cliIP,sizeof(cliIP));
unsigned short cliPort=ntohs(cliaddr.sin_port);
printf("cliIp is :%s,port is %d\n",cliIP,cliPort);
//接收客户端发来的数据
char recvBuff[1024]={0};
while(1)
{
int len1=read(cfd,&(recvBuff),sizeof(recvBuff)+1);
if(len1==-1)
{
perror("read");
exit(-1);
}
else if(len1>0)
{
printf("recive client data:%s\n",recvBuff);
}
else if(len1==0)
{
printf("client close\n");
}
write(cfd,recvBuff,strlen(recvBuff)+1);
}
close(cfd);
exit(0);
}
}
close(lfd);
return 0;
}
分析:
- 首先创建socket函数,在进行绑定,封装服务器的IP、端口号等相关信息;
- 然后进行监听,等待客户端的连接,客户端的连接是在while循环中进行的,因为需要不断接收客户端的连接;
- 连接成功一个客户端后,创建一个进程,在子进程完成服务器与客户端的通信;
- 关于子进程的回收是利用信号进行回收,因为如果利用wait函数进行回收,会造成程序在对应位置进行阻塞。
client.c客户端代码 :
//TCP通信客户端
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
//1、创建套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd==-1)
{
perror("socket");
exit(-1);
}
//2、连接服务器端
struct sockaddr_in serve_addr;
serve_addr.sin_family=AF_INET;
inet_pton(AF_INET,"192.168.254.171",&serve_addr.sin_addr.s_addr);
serve_addr.sin_port=htons(9999);
int ret= connect(fd,(const struct sockaddr *)&serve_addr,sizeof(serve_addr));
if(ret==-1)
{
perror("connect");
exit(-1);
}
//3、进行通信
char readbuff[1024]={0};
// char writebuff[1024]={0};
int i=0;
while(1)
{
// memset(writebuff,0,sizeof(writebuff));
//printf("请输入内容:\n");
//scanf("%s",writebuff);
//char * data="I am client";
sprintf(readbuff,"data:%d\n",i++);
//给服务端发送数据
write(fd, &readbuff,strlen(readbuff));
sleep(1);
int len =read(fd,readbuff,sizeof(readbuff));
if(len==-1)
{
perror("read");
exit(-1);
}else if(len>0)
{
printf("recive client data:%s\n",readbuff);
}
else if(len==0)
{
//表示服务器端断开连接。
printf("serve closed...\n");
break;
}
}
//关闭文件描述符;
close(fd);
return 0;
}
分析:
- 首先创建socket函数,然后连接服务器;
- 链接成功后进行通信,进行数据的收发;
- 最后回收文件描述符。
- 一个客户端在一个子进程中通信,这也是TCP实现多进程通信的方法原理。
二、TCP实现多线程通信
利用TCP实现多线程通信与多进程通信的区别在于,当服务端连接到客户端后,与每一个客户端进行通信时,是创建进程进行通信函数还是线程进行通信函数,也就是说客户端的代码是相同的,区别在于服务器端的代码。
server_process.c 服务端代码:
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
struct sockinfo//该结构体用于存放服务端发送给子进程关于客户端的信息,因为进程只能传递一个参数。
{
int fd;//通信文件描述符;
struct sockaddr_in addr;
pthread_t tid;//线程号;
};
struct sockinfo sockinfos[128];//定义全局变量,用于存放客户端的信息;
void * working (void *arg)
{
//获取客户端信息
struct sockinfo *pinfo=(struct sockinfo*)arg;
char cliIP[16];
inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,cliIP,sizeof(cliIP));
unsigned short cliPort=ntohs(pinfo->addr.sin_port);
printf("cliIp is :%s,port is %d\n",cliIP,cliPort);
//接收客户端发来的数据
char recvBuff[1024]={0};
while(1)
{
int len1=read(pinfo->fd,&(recvBuff),sizeof(recvBuff)+1);
if(len1==-1)
{
perror("read");
exit(-1);
}
else if(len1>0)
{
printf("recive client data:%s\n",recvBuff);
}
else if(len1==0)
{
printf("client close\n");
}
write(pinfo->fd,recvBuff,strlen(recvBuff)+1);
}
close(pinfo->fd);
exit(0);
//子线程与客户端进行通信; cfd; 客户端的信息;线程号;
return NULL;
}
int main()
{
//创建socket
int lfd =socket(AF_INET,SOCK_STREAM,0);
if(lfd==-1)
{
perror("socket");
exit(-1);
}
//绑定
struct sockaddr_in saddr;
saddr.sin_addr.s_addr=0;
saddr.sin_port=htons(9999);
saddr.sin_family=AF_INET;
int ret = bind(lfd,(const struct sockaddr*)&saddr,sizeof(saddr));
if(ret==-1)
{
perror("bind");
exit(-1);
}
//监听
ret=listen(lfd,8);
if(ret==-1)
{
perror("listen");
exit(-1);
}
// 初始化数据
int max = sizeof(sockinfos)/sizeof( sockinfos[0]);
for(int i=0;i<max;i++)
{
bzero(&sockinfos[i],sizeof(sockinfos[i]));
sockinfos[i].fd=-1;
sockinfos[i].tid=-1;
}
while(1)
{
struct sockaddr_in cliaddr;
int len =sizeof(cliaddr);
//接收连接
int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
struct sockinfo *pinfo;
for(int i=0;i<max;i++)
{
//从这个数组中可以找到一个可以用的sockInfo元素;
if (sockinfos[i].fd==-1)
{
pinfo=&sockinfos[i];
break;
}
if(i==max-1)
{
sleep(1);
i--;
}
}
pinfo->fd=cfd;
memcpy(&pinfo->addr,&cliaddr,len);
pthread_t tid;
// 每一个连接进来,创建一个子线程与客户端进行通信
pthread_create(&pinfo->tid,NULL,working,pinfo);
pthread_detach(pinfo->tid);
}
close(lfd);
return 0;
}
三、UDP实现通信
server_udp.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_PORT 9999
int server_udp()
{
int ret = 0;
int socket_fd = -1;
int addr_len = 0;
char buf[1024] = {0};
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_fd < 0) {
printf("%s: socket failed\n", __FUNCTION__);
return 0;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr_len = sizeof(server_addr);
ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);
if (ret < 0) {
printf("%s: bind failed\n", __FUNCTION__);
close(socket_fd);
return 0;
}
while (1) {
memset(buf, 0, sizeof(buf));
addr_len = sizeof(client_addr);
ret = recvfrom(socket_fd, buf, sizeof(buf), 0,
(struct sockaddr *)&client_addr, &addr_len);
if (ret > 0) {
printf("recv len:%d, data:[%s] from %s:%d\n", ret, buf,
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
} else if (0 == ret) {
printf("ret:%d\n", ret);
} else {
printf("ret:%d\n", ret);
}
}
close(socket_fd);
return 0;
}
int main(int argc, char *argv[])
{
server_udp();
return 0;
}
client_udp.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 9999
int client_udp(char *data)
{
int ret = 0;
int socket_fd = -1;
int addr_len = 0;
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_fd < 0) {
printf("%s: socket failed\n", __FUNCTION__);
return 0;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
addr_len = sizeof(server_addr);
ret = sendto(socket_fd, data, strlen(data), 0,
(struct sockaddr *)&server_addr, addr_len);
if (ret > 0) {
printf("send data: [%s] to %s:%d\n", data, inet_ntoa(server_addr.sin_addr),
ntohs(server_addr.sin_port));
} else {
printf("ret:%d\n", ret);
}
close(socket_fd);
return 0;
}
int main(int argc, char *argv[])
{
if (argc < 2) {
client_udp("hello world");
} else {
client_udp(argv[1]);
}
return 0;
}
Makefile:
all:
gcc -o server server_udp.c
gcc -o client client_udp.c
clean:
-@rm server client
编译方式:make
测试运行:
$ ./client
send data: [hello world] to 127.0.0.1:9999
$ ./server
recv len:11, data:[hello world] from 127.0.0.1:44180
$ ./client "I can do it"
send data: [I can do it] to 127.0.0.1:9999
$ ./server
recv len:11, data:[I can do it] from 127.0.0.1:43607

浙公网安备 33010602011771号