创作本文目的:记录自己的学习历程
文章目录
一、TCP循环服务器模型
1.简易版
sfd = socket();
bind();
listen();
while(1)
{
newfd = accept();
while(1)
{
recv();
send();
}
close(newfd);
}
close(sfd);
2.完整版
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <pthread.h>
//打印报错信息
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//填写服务器地址信息结构体
struct sockaddr_in ser;
ser.sin_family = AF_INET;
ser.sin_port = htons(8888);
ser.sin_addr.s_addr = inet_addr("192.168.31.178");
//绑定
if(bind(sfd,(struct sockaddr *)&ser,sizeof(ser))<0)
{
ERR_MSG("bind");
return -1;
}
//设置被动监听状态
if(listen(sfd,10)<0)
{
ERR_MSG("listen");
return -1;
}
//接收客户端地址信息结构体
struct sockaddr_in cli;
socklen_t addrlen = sizeof(cli);
char buf[128]="";
ssize_t res;
while(1)
{
//接收客户端的连接,返回文件描述符
int newfd = accept(sfd,(struct sockaddr *)&cli,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
while(1)
{
//清空字符串
bzero(buf,sizeof(buf));
//接收客户端信息
res = recv(newfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
break;
}
else if(0==res)
{
printf("[IP:%s 端口:%d] 客户端退出\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
break;
}
//显示信息.
printf("[IP:%s 端口:%d]:%s\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port),buf);
//发送信息
strcat(buf,"-.-");
if(send(newfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
break;
}
}
close(newfd);
}
close(sfd);
return 0;
}
3.运行效果
二、TCP并发服务器模型
1.多进程并发服务器
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <stdlib.h>
//打印错误信息
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
typedef void (*sighandler_t)(int);
//自定义信号处理函数
void handler(int sig)
{
//确保清除所有僵尸进程
while(waitpid(-1,NULL,WNOHANG)>0);
}
//交互函数声明
int do_recv(int,struct sockaddr_in);
int main(int argc, const char *argv[])
{
//捕获17号信号
sighandler_t s = signal(17,handler);
if(SIG_ERR == s)
{
ERR_MSG("signal");
return -1;
}
//创建流式套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//允许端口快速重用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
{
ERR_MSG("setsockopt");
return -1;
}
//填写服务器地址信息结构体
struct sockaddr_in ser;
ser.sin_family=AF_INET; //地址族
ser.sin_port=htons(8888); //端口号的网络字节序
ser.sin_addr.s_addr=inet_addr("192.168.31.178"); //IP地址的网络字节序
//绑定ip和端口到套接字上
if(bind(sfd,(struct sockaddr *)&ser,sizeof(ser))<0)
{
ERR_MSG("bind");
return -1;
}
//设置被动监听状态
if(listen(sfd,10)<0)
{
ERR_MSG("listen");
return -1;
}
//接收客户端地址信息结构体
struct sockaddr_in cli;
socklen_t addrlen = sizeof(cli);
pid_t pid = 0;
int newfd = 0;
while(1)
{
//父进程(连接)
//接收客户端的连接,返回文件描述符
newfd = accept(sfd,(struct sockaddr *)&cli,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
//创建子进程(交互)
if((pid=fork())==0)
{
//释放无用的文件描述符,不影响父进程,进程之间相互独立
close(sfd);
//交互函数
do_recv(newfd,cli);
//完成交互就退出进程
exit(0);
}
//父进程只需连接,关闭不影响子进程,进程之间相互独立
close(newfd);
}
close(sfd);
return 0;
}
int do_recv(int newfd,struct sockaddr_in cli)
{
char buf[128]="";
ssize_t res = 0;
while(1)
{
//清空字符串
bzero(buf,sizeof(buf));
//接收客户端信息
res = recv(newfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
return -1;
}
else if(0==res)
{
printf("[IP:%s 端口:%d] 客户端退出\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
break;
}
//显示信息
printf("[IP:%s 端口:%d]:%s\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port),buf);
//发送信息
strcat(buf,"-.-");
if(send(newfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
return -1;
}
}
close(newfd);
return 0;
}
运行效果
2.多线程并发服务器
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <pthread.h>
//打印错误信息
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
//需要传入到线程中的成员
struct msg
{
int newfd;
struct sockaddr_in cin;
};
//线程
pthread_t tid;
//线程任务体
void *callback(void *arg)
{
//分离分支线程
pthread_detach(pthread_self());
char buf[128]="";
ssize_t res;
int newfd = (*(struct msg *)arg).newfd;
struct sockaddr_in cin = (*(struct msg *)arg).cin;
while(1)
{
//清空字符串
bzero(buf,sizeof(buf));
//接收客户端信息
res = recv(newfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
break;
}
else if(0==res)
{
printf("[IP:%s 端口:%d] 客户端退出\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
break;
}
//显示信息.
printf("[IP:%s 端口:%d]:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),buf);
//发送信息
strcat(buf,"-.-");
if(send(newfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
break;
}
}
close(newfd);
pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//允许端口快速重用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
{
ERR_MSG("setsockopt");
return -1;
}
//填写服务器地址信息结构体
struct sockaddr_in ser;
ser.sin_family=AF_INET; //地址族
ser.sin_port=htons(8888); //端口号的网络字节序
ser.sin_addr.s_addr=inet_addr("192.168.31.178"); //IP地址的网络字节序
//绑定ip和端口到套接字上
if(bind(sfd,(struct sockaddr *)&ser,sizeof(ser))<0)
{
ERR_MSG("bind");
return -1;
}
//设置被动监听状态
if(listen(sfd,10)<0)
{
ERR_MSG("listen");
return -1;
}
//接收客户端地址信息结构体
struct sockaddr_in cli;
socklen_t addrlen = sizeof(cli);
pid_t pid = 0;
int newfd = 0;
struct msg cliInfo; //传递给线程参数
while(1)
{
//接收客户端的连接,返回文件描述符
newfd = accept(sfd,(struct sockaddr *)&cli,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
cliInfo.newfd = newfd;
cliInfo.cin = cli;
if(pthread_create(&tid,NULL,callback,(void *)&cliInfo)!=0)
{
ERR_MSG("pthread_create");
return -1;
}
}
close(sfd);
return 0;
}
运行效果
三、TCP本地通信
1.服务器
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/un.h>
//打印错误信息
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_UNIX,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//判断绑定的文件是否存在
if(access("./unix",F_OK)==0)
{
if(unlink("./unix")<0)
{
ERR_MSG("unlink");
return -1;
}
}
//填写服务器地址信息结构体
struct sockaddr_un ser;
ser.sun_family=AF_UNIX; //地址族
strcpy(ser.sun_path,"./unix");
//绑定套接字上
if(bind(sfd,(struct sockaddr *)&ser,sizeof(ser))<0)
{
ERR_MSG("bind");
return -1;
}
//设置被动监听状态
if(listen(sfd,10)<0)
{
ERR_MSG("listen");
return -1;
}
//接收客户端地址信息结构体
struct sockaddr_un cin;
socklen_t addrlen = sizeof(cin);
//从已经完成连接的队列头中,取出一个客户端信息,创建新套接字
int newfd = accept(sfd,(struct sockaddr *)&cin,&addrlen);
if(newfd<0)
{
ERR_MSG("accept");
return -1;
}
printf("newfd = %d\n",newfd);
char buf[128]="";
ssize_t res;
while(1)
{
//清空字符串
bzero(buf,sizeof(buf));
//接收客户端信息
res = recv(newfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
break;
}
else if(0==res)
{
printf("newfd:%d 客户端退出\n",newfd);
break;
}
//显示信息.
printf("newfd:%d:%s\n",newfd,buf);
//发送信息
strcat(buf,"-.-");
if(send(newfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
break;
}
}
close(sfd);
close(newfd);
return 0;
}
2.客户端
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/un.h>
//打印错误信息
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_UNIX,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//填写服务器地址信息结构体
struct sockaddr_un ser;
ser.sun_family=AF_UNIX; //地址族
strcpy(ser.sun_path,"./unix");
//连接服务器
if(connect(sfd,(struct sockaddr *)&ser,sizeof(ser))<0)
{
ERR_MSG("connect");
return -1;
}
char buf[128]="";
ssize_t res;
while(1)
{
//发送信息
bzero(buf,sizeof(buf));
printf("请输入>>>");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
if(send(sfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
break;
}
//清空字符串
bzero(buf,sizeof(buf));
//接收客户端信息
res = recv(sfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
break;
}
else if(0==res)
{
printf("服务器退出\n");
break;
}
//显示信息.
printf("%ld : %s\n",res,buf);
}
close(sfd);
return 0;
}
3.运行效果
四、UDP本地通信
1.服务器
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/un.h>
//打印错误信息
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_UNIX,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//判断绑定的文件是否存在
if(access("./udp_unix",F_OK)==0)
{
if(unlink("./udp_unix")<0)
{
ERR_MSG("unlink");
return -1;
}
}
//填写服务器地址信息结构体
struct sockaddr_un ser;
ser.sun_family=AF_UNIX; //地址族
strcpy(ser.sun_path,"./udp_unix");
//绑定套接字上
if(bind(sfd,(struct sockaddr *)&ser,sizeof(ser))<0)
{
ERR_MSG("bind");
return -1;
}
//接收客户端地址信息结构体
struct sockaddr_un cin;
socklen_t addrlen = sizeof(cin);
char buf[128]="";
ssize_t res;
while(1)
{
//清空字符串
bzero(buf,sizeof(buf));
//接收客户端信息
res = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,&addrlen);
if(res<0)
{
ERR_MSG("recvfrom");
break;
}
else if(0==res)
{
printf("sfd:%d 客户端退出\n",sfd);
break;
}
//显示信息
printf("sfd:%d:%s\n",sfd,buf);
//发送信息
strcat(buf,"-.-");
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&cin,sizeof(cin))<0)
{
ERR_MSG("sendto");
break;
}
}
close(sfd);
return 0;
}
2.客户端
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/un.h>
//打印错误信息
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
//创建报式套接字
int sfd = socket(AF_UNIX,SOCK_DGRAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
//判断要绑定的文件是否存在
if(access(argv[1],F_OK)==0)
{
if(unlink(argv[1])<0)
{
ERR_MSG("unlink");
return -1;
}
}
//填写服务器地址信息结构体
struct sockaddr_un ser;
ser.sun_family=AF_UNIX; //地址族
strcpy(ser.sun_path,"./udp_unix");
//填写客户端自身要绑定的地址信息
struct sockaddr_un cin;
cin.sun_family=AF_UNIX;
strcpy(cin.sun_path,argv[1]);
//绑定客户端
if(bind(sfd,(struct sockaddr *)&cin,sizeof(cin))<0)
{
ERR_MSG("bind");
return -1;
}
//存储接收到的数据包来自哪里
struct sockaddr_un rcv;
socklen_t addrlen = sizeof(rcv);
char buf[128]="";
ssize_t res;
while(1)
{
//发送信息
bzero(buf,sizeof(buf));
printf("请输入>>>");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr *)&ser,sizeof(ser))<0)
{
ERR_MSG("sendto");
break;
}
//清空字符串
bzero(buf,sizeof(buf));
//接收客户端信息
res = recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&ser,&addrlen);
if(res<0)
{
ERR_MSG("recv");
break;
}
else if(0==res)
{
printf("服务器退出\n");
break;
}
//显示信息.
printf("%s : %s\n",ser.sun_path,buf);
}
close(sfd);
return 0;
}