网络结构模式
C/S结构
B/S结构
MAC地址、IP地址、端口
网卡
MAC地址
IP地址
子网掩码
端口
网络类型
OSI七层参考模型
TCP/IP四层模型
协议
常见协议
UDP协议
TCP协议
IP协议
以太网帧协议
ARP协议
网络通信过程
封装
分用
socket
socket介绍
字节序
简介
举例
小端存储
大端存储
/*
字节序:字节在内存中存储的顺序
小端字节序:数据的高位字节存储在内存的高位地址,低位字节存储在内存的低位地址。
大端字节序:数据的低位字节存储在内存的高位地址,高位字节存储在内存的低位地址。
*/
//通过代码检测当前主机的字节序
#include<stdio.h>
int main()
{
union
{
short value; //2字节
char bytes[sizeof(short)]; //char[2]
}test;
test.value = 0x0102;
if(test.bytes[0] == 1 && test.bytes[1] == 2)
{
printf("大端字节序\n");
}
else if(test.bytes[0] == 2 && test.bytes[1] == 1)
{
printf("小端字节序\n");
}
else
{
printf("未知\n");
}
return 0;
}
字节序转换函数
示例:
/*
网络通信时,需要将主机字节序转换成网络字节序(大端),
另外一端获取到数据以后根据情况将网络字节序转换成主机字节序
*/
#include<stdio.h>
#include<arpa/inet.h>
int main()
{
//htons 转换端口
unsigned short a = 0x0102;
printf("%x\n",a);
unsigned short b = htons(a);
printf("%x\n",b);
printf("=======================================\n");
//htonl 转换IP
char buf[4] = {192,168,1,100};
int num = *(int*)buf;
int sum = htonl(num);
unsigned char *p = (char*)∑
printf("%d,%d,%d,%d\n",*p,*(p+1),*(p+2),*(p+3));
printf("=======================================\n");
//ntohl
unsigned char buf1[4] = {1,1,168,192};
int num1 = *(int*)buf1;
int sum1 = ntohl(num1);
unsigned char* p1 = (unsigned char*)&sum1;
printf("%d,%d,%d,%d\n",*p1,*(p1+1),*(p1+2),*(p1+3));
return 0;
}
socket地址
通用socket地址
专用socket地址
IP地址转换函数
示例:
#include<stdio.h>
#include<arpa/inet.h>
int main()
{
//创建一个ip字符串,点分十进制的ip地址字符串
char buf[] = "192.168.1.4";
unsigned int num = 0;
//将点分十进制的ip字符串转换成网络字节序的整数
inet_pton(AF_INET,buf,&num);
unsigned char* p = (unsigned char*)#
printf("%d,%d,%d,%d\n",*p,*(p+1),*(p+2),*(p+3));
//将网络字节序的ip整数转换成点分十进制的ip字符串
char ip[16] = "";
const char* str = inet_ntop(AF_INET,&num,ip,16);
printf("str : %s\n",str);
printf("ip : %s\n",ip);
return 0;
}
TCP通信流程
套接字函数
server
//TCP通信的服务器端
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
//1.创建socket(用于监听的套接字)
int lfd = socket(AF_INET,SOCK_STREAM,0);
if(lfd == -1)
{
perror("socket");
exit(-1);
}
//2.绑定
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
//inet_pton(AF_INET,"192.168.43.130",saddr.sin_addr.s_addr);
saddr.sin_addr.s_addr = INADDR_ANY;//0.0.0.0
saddr.sin_port = htons(9999);
int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
//3.监听
ret = listen(lfd,8);
if(ret == -1)
{
perror("listen");
exit(-1);
}
//4.接受客户端连接
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
int cfd = accept(lfd,(struct sockaddr*)&clientaddr,&len);
if(cfd == -1)
{
perror("accept");
exit(-1);
}
//输出客户端的信息
char clientIP[16];
inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientIP,sizeof(clientIP));
unsigned short clientPort = ntohs(clientaddr.sin_port);
printf("client ip is : %s ; port is : %d \n",clientIP,clientPort);
//5.通信
char recvBuf[1024]={0};
while(1)
{
//获取客户端的数据
int len1 = read(cfd,recvBuf,sizeof(recvBuf));
if(len1 == -1)
{
perror("read");
exit(-1);
}
else if(len1 > 0)
{
printf("recv client data : %s\n",recvBuf);
}
else if(len1 == 0)
{
//表示客户端断开连接
printf("client closed...");
break;
}
//给客户端发送数据
char* data="hello, i am server";
write(cfd,data,strlen(data));
}
//关闭文件描述符
close(cfd);
close(lfd);
return 0;
}
client
//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 serveraddr;
serveraddr.sin_family = AF_INET;
inet_pton(AF_INET,"192.168.43.130",&serveraddr.sin_addr.s_addr);
serveraddr.sin_port = htons(9999);
int ret = connect(fd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(ret == -1)
{
perror("connect");
exit(-1);
}
//3.通信
char recvBuf[1024]={0};
while(1)
{
//给服务器端发送数据
char* data="hello, i am client";
write(fd,data,strlen(data));
sleep(1);
//获取服务器端的数据
int len = read(fd,recvBuf,sizeof(recvBuf));
if(len == -1)
{
perror("read");
exit(-1);
}
else if(len > 0)
{
printf("recv server data : %s\n",recvBuf);
}
else if(len == 0)
{
//表示服务器端断开连接
printf("server closed...");
break;
}
}
//关闭连接
close(fd);
return 0;
}
TCP三次握手
TCP滑动窗口
TCP四次挥手
TCP通信并发
多进程实现并发服务器
server端代码:
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.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(PF_INET,SOCK_STREAM,0);
if(lfd == -1)
{
perror("socket");
exit(-1);
}
//绑定
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9998);
saddr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
//监听
ret = listen(lfd,128);
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("client ip is : %s , port is %d \n",cliIP,cliPort);
//接收客户端发来的数据
char recvBuf[1024] = {0};
while(1)
{
int len = read(cfd,&recvBuf,sizeof(recvBuf));
if(len == -1)
{
perror("read");
exit(-1);
}
else if(len > 0)
{
printf("recv client data : %s\n",recvBuf);
}
else if(len == 0)
{
printf("client closed...\n");
break;
}
write(cfd,recvBuf,strlen(recvBuf)+1);
}
close(cfd);
exit(0); //退出当前子进程
}
}
close(lfd);
return 0;
}
client端代码:
//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 serveraddr;
serveraddr.sin_family = AF_INET;
inet_pton(AF_INET,"192.168.43.131",&serveraddr.sin_addr.s_addr);
serveraddr.sin_port = htons(9998);
int ret = connect(fd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
if(ret == -1)
{
perror("connect");
exit(-1);
}
//3.通信
char recvBuf[1024]={0};
int i=0;
while(1)
{
//给服务器端发送数据
sprintf(recvBuf,"data : %d \n",i++);
write(fd,recvBuf,strlen(recvBuf)+1);
//获取服务器端的数据
int len = read(fd,recvBuf,sizeof(recvBuf));
if(len == -1)
{
perror("read");
exit(-1);
}
else if(len > 0)
{
printf("recv server data : %s\n",recvBuf);
}
else if(len == 0)
{
//表示服务器端断开连接
printf("server closed...");
break;
}
sleep(1);
}
//关闭连接
close(fd);
return 0;
}
多线程实现并发服务器
服务端
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
struct sockInfo
{
int fd; //通信的文件描述符
pthread_t tid; //线程号
struct sockaddr_in addr;
};
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("client ip is : %s , port is %d \n",cliIP,cliPort);
//接收客户端发来的数据
char recvBuf[1024] = {0};
while(1)
{
int len = read(pinfo->fd,&recvBuf,sizeof(recvBuf));
if(len == -1)
{
perror("read");
exit(-1);
}
else if(len > 0)
{
printf("recv client data : %s\n",recvBuf);
}
else if(len == 0)
{
printf("client closed...\n");
break;
}
write(pinfo->fd,recvBuf,strlen(recvBuf)+1);
}
close(pinfo->fd);
return NULL;
}
int main()
{
//初始化数据
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;
}
//创建socket
int lfd = socket(PF_INET,SOCK_STREAM,0);
if(lfd == -1)
{
perror("socket");
exit(-1);
}
//绑定
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9998);
saddr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
//监听
ret = listen(lfd,128);
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);
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=-1;
}
}
pinfo->fd=cfd;
memcpy(&pinfo->addr,&cliaddr,len);
//创建子线程
pthread_create(&pinfo->tid,NULL,working,pinfo);
//释放
pthread_detach(pinfo->tid);
}
close(lfd);
return 0;
}
客户端不变
TCP状态转换
TIME_WAIT经过2MSL变成CLOSED
确保通信的另一方能够接收到最后一次的ACK
半关闭
一方close确认了,另一方还没close;
端口复用
I/O多路复用(I/O多路转接)
此处I/O指的是对缓冲区的操作
两种待改进的模型:
1.阻塞等待
BIO模型:使用多线程缓解阻塞等待
accept、read、recv都是阻塞的
2.非阻塞、忙轮询
NIO模型:通过遍历轮询
使用IO多路转接技术进行改进,委托内核来检测。
select
select服务器
#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/select.h>
int main()
{
//创建socket
int lfd = socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in saddr;
saddr.sin_port = htons(9999);
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
//绑定
bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
//监听
listen(lfd,8);
//创建一个fd_set的集合,存放的是需要检测的文件描述符
fd_set rdset,tmp; //可以存1024个文件描述符
FD_ZERO(&rdset);
FD_SET(lfd,&rdset);
int maxfd = lfd;
while(1)
{
tmp = rdset;
//调用select系统函数,让内核帮忙检测哪些文件描述符有数据
int ret = select(maxfd+1,&tmp,NULL,NULL,NULL);
if(ret == -1)
{
perror("select");
exit(-1);
}
else if(ret == 0)
{
continue;
}
else if(ret > 0)
{
//说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
//ret只返回有几个发生了改变,具体是哪几个需要我们自己去查
if(FD_ISSET(lfd,&tmp))
{
//表示有新的客户端连接进来了
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
//将新的文件描述符加入到集合中,并更新maxfd
FD_SET(cfd,&rdset);
maxfd = maxfd>cfd?maxfd:cfd;
}
for(int i=lfd+1;i<=maxfd;i++)
{
if(FD_ISSET(i,&tmp))
{
//说明这个文件描述符对应的客户端发来了数据
char buf[1024] = {0};
int len = read(i,buf,sizeof(buf));
if(len == -1)
{
perror("read");
exit(-1);
}
else if(len == 0)
{
printf("client closed ... \n");
close(i);
FD_CLR(i,&rdset);
}
else if(len > 0)
{
printf("read buf = %s\n",buf);
write(i,buf,strlen(buf)+1);
}
}
}
}
}
close(lfd);
return 0;
}
poll
poll服务器
#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<poll.h>
int main()
{
//创建socket
int lfd = socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in saddr;
saddr.sin_port = htons(9999);
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
//绑定
bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
//监听
listen(lfd,8);
//初始化检测的文件描述符数组
struct pollfd fds[1024];
for(int i=0;i<1024;i++)
{
fds[i].fd = -1;
fds[i].events = POLLIN;
}
fds[0].fd = lfd;
int nfds = 0;
while(1)
{
//调用poll系统函数,让内核帮忙检测哪些文件描述符有数据
int ret = poll(fds,nfds+1,-1);
if(ret == -1)
{
perror("poll");
exit(-1);
}
else if(ret == 0)
{
continue;
}
else if(ret > 0)
{
//说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
//ret只返回有几个发生了改变,具体是哪几个需要我们自己去查
if(fds[0].revents & POLLIN)
{
//表示有新的客户端连接进来了
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
//将新的文件描述符加入到集合中,并更新最大文件描述符的索引
for(int i=1;i<1024;i++)
{
if(fds[i].fd == -1)
{
fds[i].fd = cfd;
fds[i].events = POLLIN;
break;
}
}
nfds = nfds>cfd?nfds:cfd;
}
for(int i=1;i<=nfds;i++)
{
if(fds[i].revents & POLLIN)
{
//说明这个文件描述符对应的客户端发来了数据
char buf[1024] = {0};
int len = read(fds[i].fd,buf,sizeof(buf));
if(len == -1)
{
perror("read");
exit(-1);
}
else if(len == 0)
{
printf("client closed ... \n");
close(fds[i].fd);
fds[i].fd = -1;
}
else if(len > 0)
{
printf("read buf = %s\n",buf);
write(fds[i].fd,buf,strlen(buf)+1);
}
}
}
}
}
close(lfd);
return 0;
}
epoll
epoll服务器:
#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/epoll.h>
int main()
{
//创建socket
int lfd = socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in saddr;
saddr.sin_port = htons(9999);
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
//绑定
bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
//监听
listen(lfd,8);
//用epoll_create()创建一个epoll实例
int epfd = epoll_create(100);
//将监听的文件描述符相关的监测信息添加到epoll实例中
struct epoll_event epev;
epev.events = EPOLLIN;
epev.data.fd = lfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);
struct epoll_event epevs[1024];
while(1)
{
int ret = epoll_wait(epfd,epevs,1024,-1);
if(ret == -1)
{
perror("epoll_wait");
exit(-1);
}
printf("ret = %d\n",ret);
for(int i=0;i<ret;i++)
{
int curfd = epevs[i].data.fd;
if(curfd == lfd)
{
//监听的文件描述符有数据到达,有客户端连接
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
epev.events = EPOLLIN;
epev.data.fd = cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
}
else
{
//有数据到达,需要通信
char buf[1024] = {0};
int len = read(curfd,buf,sizeof(buf));
if(len == -1)
{
perror("read");
exit(-1);
}
else if(len == 0)
{
printf("client closed ... \n");
epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
close(curfd);
}
else if(len > 0)
{
printf("read buf = %s\n",buf);
write(curfd,buf,strlen(buf)+1);
}
}
}
}
close(lfd);
close(epfd);
return 0;
}
epoll的两种工作模式
设置边沿触发需要设置EPOLLET
ET模式服务器
#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/epoll.h>
#include<fcntl.h>
#include<errno.h>
int main()
{
//创建socket
int lfd = socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in saddr;
saddr.sin_port = htons(9999);
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
//绑定
bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
//监听
listen(lfd,8);
//用epoll_create()创建一个epoll实例
int epfd = epoll_create(100);
//将监听的文件描述符相关的监测信息添加到epoll实例中
struct epoll_event epev;
epev.events = EPOLLIN;
epev.data.fd = lfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);
struct epoll_event epevs[1024];
while(1)
{
int ret = epoll_wait(epfd,epevs,1024,-1);
if(ret == -1)
{
perror("epoll_wait");
exit(-1);
}
printf("ret = %d\n",ret);
for(int i=0;i<ret;i++)
{
int curfd = epevs[i].data.fd;
if(curfd == lfd)
{
//监听的文件描述符有数据到达,有客户端连接
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
//设置cfd属性非阻塞
int flag = fcntl(cfd,F_GETFL);
flag = flag | O_NONBLOCK;
fcntl(cfd,F_SETFL,flag);
epev.events = EPOLLIN | EPOLLET;
epev.data.fd = cfd;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
}
else
{
//有数据到达,需要通信
//循环读取所有数据
//上文中已将文件描述符设为非阻塞
char buf[5];
int len = 0;
while((len = read(curfd,buf,sizeof(buf))) > 0)
{
//打印数据
printf("recv data : %s\n",buf);
write(curfd,buf,len);
}
if(len == 0)
{
printf("client closed ...\n");
}
else if(len == -1)
{
if(errno == EAGAIN)
{
printf("data read over ...\n");
}
else
{
perror("read");
exit(-1);
}
}
}
}
}
close(lfd);
close(epfd);
return 0;
}
UDP通信
服务端:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
//1.创建一个通信的socket
int fd = socket(PF_INET,SOCK_DGRAM,0);
if(fd == -1)
{
perror("socket");
exit(-1);
}
//2.绑定
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);
addr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
//3.通信
while(1)
{
char recvbuf[128];
char ipbuf[16];
struct sockaddr_in cliaddr;
int len =sizeof(cliaddr);
//接收数据
int num = recvfrom(fd,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&cliaddr,&len);
printf("client IP : %s, PORT : %d\n",
inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,ipbuf,sizeof(ipbuf)),
ntohs(cliaddr.sin_port)
);
printf("client say : %s\n",recvbuf);
//发送数据
sendto(fd,recvbuf,strlen(recvbuf)+1,0,(struct sockaddr*)&cliaddr,sizeof(cliaddr));
}
close(fd);
return 0;
}
客户端:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
//1.创建一个通信的socket
int fd = socket(PF_INET,SOCK_DGRAM,0);
if(fd == -1)
{
perror("socket");
exit(-1);
}
//服务器的地址信息
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9999);
inet_pton(AF_INET,"127.0.0.1",&saddr.sin_addr.s_addr);
int num=0;
//2.通信
while(1)
{
//发送数据
char sendbuf[128];
sprintf(sendbuf,"hello , i am client %d\n",num++);
sendto(fd,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&saddr,sizeof(saddr));
//接收数据
recvfrom(fd,sendbuf,sizeof(sendbuf),0,NULL,NULL);
printf("server say : %s\n",sendbuf);
sleep(1);
}
close(fd);
return 0;
}
广播
服务器代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
//1.创建一个通信的socket
int fd = socket(PF_INET,SOCK_DGRAM,0);
if(fd == -1)
{
perror("socket");
exit(-1);
}
//2.设置广播属性
int op = 1;
setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&op,sizeof(op));
//2.创建一个广播的地址
struct sockaddr_in cliaddr;
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(9999);
inet_pton(AF_INET,"192.168.43.255",&cliaddr.sin_addr.s_addr);
//3.通信
int num = 0;
while(1)
{
char sendbuf[128];
sprintf(sendbuf,"hello, client ...%d\n",num++);
//发送数据
sendto(fd,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&cliaddr,sizeof(cliaddr));
printf("广播的数据:%s\n",sendbuf);
sleep(1);
}
close(fd);
return 0;
}
客户端代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
//1.创建一个通信的socket
int fd = socket(PF_INET,SOCK_DGRAM,0);
if(fd == -1)
{
perror("socket");
exit(-1);
}
//客户端绑定本地的IP和端口
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9999);
saddr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(fd,(struct sockaddr*)&saddr,sizeof(saddr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
int num=0;
//2.通信
while(1)
{
char buf[128];
//接收数据
recvfrom(fd,buf,sizeof(buf),0,NULL,NULL);
printf("server say : %s\n",buf);
}
close(fd);
return 0;
}
组播(多播)
服务器代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
//1.创建一个通信的socket
int fd = socket(PF_INET,SOCK_DGRAM,0);
if(fd == -1)
{
perror("socket");
exit(-1);
}
//2.设置多播属性,设置外出接口
struct in_addr imr_multiaddr;
//初始化多播地址
inet_pton(AF_INET,"239.0.0.10",&imr_multiaddr.s_addr);
setsockopt(fd,IPPROTO_IP,IP_MULTICAST_IF,&imr_multiaddr,sizeof(imr_multiaddr));
//3.初始化客户端的地址信息
struct sockaddr_in cliaddr;
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(9999);
inet_pton(AF_INET,"239.0.0.10",&cliaddr.sin_addr.s_addr);
//3.通信
int num = 0;
while(1)
{
char sendbuf[128];
sprintf(sendbuf,"hello, client ...%d\n",num++);
//发送数据
sendto(fd,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&cliaddr,sizeof(cliaddr));
printf("组播的数据:%s\n",sendbuf);
sleep(1);
}
close(fd);
return 0;
}
客户端代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
//1.创建一个通信的socket
int fd = socket(PF_INET,SOCK_DGRAM,0);
if(fd == -1)
{
perror("socket");
exit(-1);
}
//客户端绑定本地的IP和端口
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9999);
saddr.sin_addr.s_addr = INADDR_ANY;
int ret = bind(fd,(struct sockaddr*)&saddr,sizeof(saddr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
struct ip_mreq op;
inet_pton(AF_INET,"239.0.0.10",&op.imr_multiaddr.s_addr);
op.imr_interface.s_addr = INADDR_ANY;
//加入到多播组
setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&op,sizeof(op));
//2.通信
while(1)
{
char buf[128];
//接收数据
recvfrom(fd,buf,sizeof(buf),0,NULL,NULL);
printf("server say : %s\n",buf);
}
close(fd);
return 0;
}
本地套接字
流程:
服务端代码:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<sys/un.h>
int main()
{
//先把伪文件删了
unlink("server.sock");
//1.创建监听的套接字
int lfd = socket(AF_LOCAL,SOCK_STREAM,0);
if(lfd == -1)
{
perror("socket");
exit(-1);
}
//2.绑定本地套接字文件
struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path,"server.sock");
int ret = bind(lfd,(struct sockaddr*)&addr,sizeof(addr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
//3.监听
ret = listen(lfd,100);
if(ret == -1)
{
perror("listen");
exit(-1);
}
//4.等待客户端连接
struct sockaddr_un cliaddr;
int len = sizeof(cliaddr);
int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
if(cfd == -1)
{
perror("accept");
exit(-1);
}
printf("client socket filename : %s\n",cliaddr.sun_path);
//5.通信
while (1)
{
char buf[128];
int len = recv(cfd, buf, sizeof(buf),0);
if(len == -1)
{
perror("recv");
exit(-1);
}
else if(len == 0)
{
printf("client closed ...\n");
break;
}
else if(len > 0)
{
printf("client say : %s\n",buf);
send(cfd,buf,len,0);
}
}
close(cfd);
close(lfd);
return 0;
}
客户端代码:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<sys/un.h>
int main()
{
//先把伪文件删了
unlink("client.sock");
//1.创建套接字
int cfd = socket(AF_LOCAL,SOCK_STREAM,0);
if(cfd == -1)
{
perror("socket");
exit(-1);
}
//2.绑定本地套接字文件
struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy(addr.sun_path,"client.sock");
int ret = bind(cfd,(struct sockaddr*)&addr,sizeof(addr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
//3.连接服务器
struct sockaddr_un seraddr;
seraddr.sun_family = AF_LOCAL;
strcpy(seraddr.sun_path,"server.sock");
ret = connect(cfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
if(ret == -1)
{
perror("connect");
exit(-1);
}
//4.通信
int num = 0;
while (1)
{
//发送数据
char buf[128];
sprintf(buf,"hello, i am client %d\n",num++);
send(cfd,buf,strlen(buf)+1,0);
printf("client say : %s\n",buf);
//接收数据
int len = recv(cfd, buf, sizeof(buf),0);
if(len == -1)
{
perror("recv");
exit(-1);
}
else if(len == 0)
{
printf("server closed ...\n");
break;
}
else if(len > 0)
{
printf("server say : %s\n",buf);
}
sleep(1);
}
close(cfd);
return 0;
}