对比tcp和udp
- tcp面向连接可靠的通信方式
面向连接:tcp客户端在连接服务器的时候,tcp底层会经历三次握手
三次握手:目的就是通信双方互相确认身份
客户端连接服务器的时候
四次握手:tcp通信双方断开连接的时候
可靠:tcp协议支持错误重传,校验数据
不容易丢失数据包
应用场合:通信要求比较高,传输账号密码 tcp占用的带宽资源多
udp无连接的不可靠的通信方式
无连接:没有三次握手的过程
不可靠:网络状况不好的时候,容易丢失数据包
应用场合:比如视频点播,udp占用的带宽资源少
多路复用
- 作用:监测文件描述符的状态变化(监测文件描述符是否有数据可读写)
状态变化: 有数据可读,有数据可写,异常
多路复用代码的实现思路:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 思想类似
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
有可能需要监测多个文件描述符–》多个文件描述符该如何存储–》linux中定义了一个变量类型fd_set (文件描述符集合, 专门用来存放要监测的文件描述符)
第一步:定义文件描述符集合变量
fd_set myset;
第二步:往集合中添加要监测的文件描述符
FD_ZERO(&myset);
FD_SET(你要监测的文件描述符, &myset);
第三步:调用select去监测刚才你添加的文件描述符状态
第四步(重点):判断你监测的文件描述符是否发生了状态改变
通过判断文件描述符在不在集合中即可实现
if(FD_ISSET(文件描述符,&myset)) //说明文件描述符在集合中–》说明是这个文件描述符发生了状态改变
{
}
-
相关的接口函数
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
返回值:成功 >0
失败 -1
超时 0
参数:nfds(重点) --》你要监测的所有的文件描述符中最大的文件描述符+1
readfds --》我想监测文件描述符是否有数据可读
select(最大+1,&myset,NULL,NULL) //表示我只想监测myset中的文件描述符是否有数据可读
writefds --》我想监测文件描述符是否有数据可写
select(最大+1,NULL,&myset,NULL) //表示我只想监测myset中的文件描述符是否有数据可写
exceptfds --》我想监测文件描述符是否发生异常
select(最大+1,NULL,NULL,&myset) //表示我只想监测myset中的文件描述符是否发生异常
timeout --》超时时间,设置NULL表示永久阻塞等待
struct timeval
{
tv_sec; //秒
tv_usec; //微秒
}
特点:
第一:select阻塞当前程序,直到想要监测的文件描述符发生了状态改变才解除阻塞
第二:如果select监测的了多个文件描述符(比如:A,B,C三个),如果某个文件描述符发生了状态改变,那么select会自动将没有发生状态改变的文件描述符从集合中剔除
也就是说:select会将发生状态改变的文件描述符保留在集合,其它的删除void FD_CLR(int fd, fd_set *set); //从set中把fd删除
int FD_ISSET(int fd, fd_set *set); //判断fd在不在set集合中 返回1 在集合中 返回0 不在集合中
void FD_SET(int fd, fd_set *set); //把fd添加都set集合中
void FD_ZERO(fd_set *set); //清空set集合- 以tcp单向通信代码为例,演示多路复用使用的套路
客户端发送信息给服务器
客户端使用多路复用的思路
fd_set myset;
FD_ZERO(&myset);
FD_SET(键盘0, &myset); //不会写,想不清楚究竟要监测哪些文件描述符
思考方向:根据你代码的功能去思考–》客户端需要使用键盘输入(要监测键盘)
select(0+1,&myset,NULL,NULL,NULL);
- 以tcp单向通信代码为例,演示多路复用使用的套路
select实现tcp单向通信
#include "myhead.h"
int main()
{
int tcpsock;
int newsock;
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
printf("旧的文件描述符:%dn",tcpsock);
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!n");
return -1;
}
//接收客户端的连接请求
newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
if(newsock==-1)
{
perror("接收客户端的连接请求失败!n");
return -1;
}
//定义文件描述符集合
fd_set myset;
FD_ZERO(&myset);
FD_SET(newsock,&myset);
while(1)
{
//调用select监测
ret=select(newsock+1,&myset,NULL,NULL,NULL);
if(ret>0) //说明监测到了状态改变
{
//进一步判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(newsock,&myset))
{
bzero(buf,100);
read(newsock,buf,100);
printf("客户端发送过来的信息是:%sn",buf);
}
}
else
{
perror("select失败!n");
return -1;
}
}
}
#include "myhead.h"
int main()
{
int tcpsock;
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
//定义结构体变量存放对方(服务器)的ip和端口号
struct sockaddr_in serveraddr;
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//连接服务器
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1)
{
perror("连接服务器!n");
return -1;
}
//定义文件描述符集合变量
fd_set myset;
//添加要监测的文件描述符
FD_ZERO(&myset);
FD_SET(0,&myset);
printf("阻塞在select!n");
while(1)
{
//调用select去监测 电子警察
ret=select(0+1,&myset,NULL,NULL,NULL);
if(ret>0) //监测成功,说明有文件描述符发生了状态变化
{
//进一步去判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(0,&myset))
{
printf("相信我,键盘一定有输入,不骗你的!n");
//主动调用scanf读取键盘输入的内容
scanf("%s",buf);
//发送给服务器
write(tcpsock,buf,strlen(buf));
}
}
else //失败
{
perror("select监测失败!n");
return -1;
}
}
close(tcpsock);
return 0;
}
select实现双向通信
#include "myhead.h"
int main()
{
int tcpsock;
int newsock;
int ret;
char buf[100];
char otherbuf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
printf("旧的文件描述符:%dn",tcpsock);
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!n");
return -1;
}
//接收客户端的连接请求
newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
if(newsock==-1)
{
perror("接收客户端的连接请求失败!n");
return -1;
}
//定义文件描述符集合
fd_set myset;
while(1)
{
FD_ZERO(&myset);
FD_SET(0,&myset);
FD_SET(newsock,&myset); //一定要放在循环中,因为select会自动剔除没有发生状态改变的文件描述符
//调用select监测
ret=select(newsock+1,&myset,NULL,NULL,NULL);
if(ret>0) //说明监测到了状态改变
{
//进一步判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(newsock,&myset))
{
bzero(buf,100);
read(newsock,buf,100);
printf("客户端发送过来的信息是:%sn",buf);
}
//进一步判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(0,&myset))
{
bzero(otherbuf,100);
//获取键盘输入的字符串,发送给客户端
scanf("%s",otherbuf);
write(newsock,otherbuf,strlen(otherbuf));
}
}
else
{
perror("select失败!n");
return -1;
}
}
}
#include "myhead.h"
int main()
{
int tcpsock;
int ret;
char buf[100];
char otherbuf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
//定义结构体变量存放对方(服务器)的ip和端口号
struct sockaddr_in serveraddr;
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//连接服务器
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1)
{
perror("连接服务器!n");
return -1;
}
//定义文件描述符集合变量
fd_set myset;
while(1)
{
//添加要监测的文件描述符
FD_ZERO(&myset);
FD_SET(0,&myset);
FD_SET(tcpsock,&myset); //一定要放在循环中,因为select会自动剔除没有发生状态改变的文件描述符
//调用select去监测 电子警察
ret=select(tcpsock+1,&myset,NULL,NULL,NULL);
if(ret>0) //监测成功,说明有文件描述符发生了状态变化
{
//进一步去判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(0,&myset))
{
printf("相信我,键盘一定有输入,不骗你的!n");
//主动调用scanf读取键盘输入的内容
scanf("%s",buf);
//发送给服务器
write(tcpsock,buf,strlen(buf));
}
//进一步去判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(tcpsock,&myset))
{
bzero(otherbuf,100);
//读取服务器发送过来的信息
read(tcpsock,otherbuf,100);
printf("服务器发送过来的信息是:%sn",otherbuf);
}
}
else //失败
{
perror("select监测失败!n");
return -1;
}
}
close(tcpsock);
return 0;
}
UDP组播
#include "myhead.h"
int main()
{
char buf[100];
int ret;
int udpsock;
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定自己的ip地址
bindaddr.sin_port=htons(10086); //绑定自己的端口号
struct sockaddr_in otheraddr;
bzero(&otheraddr,sizeof(otheraddr));
otheraddr.sin_family=AF_INET;
otheraddr.sin_addr.s_addr=inet_addr("225.168.24.10"); //组播专用地址 D类地址
otheraddr.sin_port=htons(20000); //对方的端口号
//创建udp套接字
udpsock=socket(PF_INET,SOCK_DGRAM,0);
if(udpsock==-1)
{
perror("创建udp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(udpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(udpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//设置套接字的属性为可以广播
setsockopt(udpsock,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
//发送广播信息
while(1)
{
bzero(buf,100);
printf("请输入要发送组播的信息!n");
scanf("%s",buf);
sendto(udpsock,buf,strlen(buf),0,(struct sockaddr *)&otheraddr,sizeof(otheraddr));
}
}
#include "myhead.h"
int main()
{
char buf[100];
char ip[16]={0};
int ret;
int udpsock;
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定自己的ip地址
bindaddr.sin_port=htons(20000); //绑定自己的端口号
struct sockaddr_in otheraddr;
bzero(&otheraddr,sizeof(otheraddr));
int size=sizeof(otheraddr);
//创建udp套接字
udpsock=socket(PF_INET,SOCK_DGRAM,0);
if(udpsock==-1)
{
perror("创建udp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(udpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(udpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//加入组播组
struct ip_mreq mreq; //定义结构体变量存放组播地址
bzero(&mreq, sizeof(mreq));
mreq.imr_multiaddr.s_addr = inet_addr("225.168.24.10"); // D类地址
mreq.imr_interface.s_addr = htonl(INADDR_ANY); // 必须用这个宏定义
setsockopt(udpsock, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq));
//接收信息
while(1)
{
bzero(buf,100);
recvfrom(udpsock,buf,100,0,(struct sockaddr *)&otheraddr,&size);
printf("ip %s 端口是 %hu 这个人发送的信息是:%sn",inet_ntop(AF_INET,&(otheraddr.sin_addr.s_addr),ip,sizeof(ip)),ntohs(otheraddr.sin_port),buf);
}
}
多路复用实现TCP多路广播
#include "myhead.h"
int main()
{
int tcpsock;
int newsock[50]={0}; //最多可以存放50个新的客户端的套接字
int i,j;
int n=0; //统计目前有多少给连接成功的客户端
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
printf("旧的文件描述符:%dn",tcpsock);
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!n");
return -1;
}
//定义集合变量
fd_set myset;
int maxfd=tcpsock; //保存最大文件描述符
while(1)
{
FD_ZERO(&myset);
FD_SET(tcpsock,&myset);
//把目前所有新的套接字添加到集合中(关键点)
for(i=0; i0)
{
if(FD_ISSET(tcpsock,&myset)) //说明tcpsock发生了读就绪--》有新的客户端连接服务器
{
//主动调用接收客户端的连接请求
newsock[n]=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
if(newsock[n]==-1)
{
perror("接收客户端的连接请求失败!n");
return -1;
}
printf("新套接字是:%dn",newsock[n]);
//求集合中最大的文件描述符
if(newsock[n]>maxfd)
maxfd=newsock[n];
n++;
}
//循环判断之前添加的文件描述符在不在集合中
for(i=0; i
if(FD_ISSET(newsock[i],&myset)) //说明新的套接字发生了读就绪--》有新的客户端发送信息给服务器了
{
bzero(buf,100);
//接收客户端的信息
recv(newsock[i],buf,100,0);
printf("服务器接收的信息是:%sn",buf);
//转发给其它所有的客户端
for(j=0; j
send(newsock[j],buf,strlen(buf),0);
}
}
}
}
else{
perror("select失败!n");
return -1;
}
}
}在这里插入代码片
#include "myhead.h"
int main()
{
int tcpsock;
int ret;
char buf[100];
char otherbuf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(20000); //绑定客户端自己的端口号
//定义结构体变量存放对方(服务器)的ip和端口号
struct sockaddr_in serveraddr;
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//连接服务器
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1)
{
perror("连接服务器!n");
return -1;
}
//定义文件描述符集合变量
fd_set myset;
while(1)
{
//添加要监测的文件描述符
FD_ZERO(&myset);
FD_SET(0,&myset);
FD_SET(tcpsock,&myset); //一定要放在循环中,因为select会自动剔除没有发生状态改变的文件描述符
//调用select去监测 电子警察
ret=select(tcpsock+1,&myset,NULL,NULL,NULL);
if(ret>0) //监测成功,说明有文件描述符发生了状态变化
{
//进一步去判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(0,&myset))
{
printf("相信我,键盘一定有输入,不骗你的!n");
//主动调用scanf读取键盘输入的内容
scanf("%s",buf);
//发送给服务器
write(tcpsock,buf,strlen(buf));
}
//进一步去判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(tcpsock,&myset))
{
bzero(otherbuf,100);
//读取服务器发送过来的信息
read(tcpsock,otherbuf,100);
printf("服务器发送过来的信息是:%sn",otherbuf);
}
}
else //失败
{
perror("select监测失败!n");
return -1;
}
}
close(tcpsock);
return 0;
}
TCP广播
#include "myhead.h"
pthread_attr_t myattr;
void fun(int num)
{
pthread_attr_destroy(&myattr);
exit(0);
}
//定义内核链表
struct clientlist
{
char ip[16]; //存放ip
unsigned short someport; //存放端口号
int sock; //存放套接字
struct list_head mypoint;
};
struct clientlist *myhead;
//初始化链表头
struct clientlist *init_list()
{
struct clientlist *head=malloc(sizeof(struct clientlist));
INIT_LIST_HEAD(&(head->mypoint));
return head;
}
void *routine(void *arg)
{
int ret;
char msg[100];
char buf[100];
struct clientlist *p=(struct clientlist *)arg;
struct clientlist *pos,*n;
char allmsg[2048]={0};
while(1)
{
bzero(buf,100);
bzero(msg,100);
bzero(allmsg,2048);
ret=read(p->sock,buf,100); //接收某个客户端发送的信息
if(ret==0) //说明客户端退出
{
//删除断开的客户端
list_for_each_entry_safe(pos, n,&(myhead->mypoint),mypoint)
{
if(pos->sock==p->sock)
list_del(&(pos->mypoint));
}
break;
}
printf("客户端%s,端口号:%hu 发送过来的信息是:%sn",p->ip,p->someport,buf);
//判断收到的信息是不是getlist
if(strcmp(buf,"getlist")==0)
{
//遍历链表,把链表中存放的信息拼接一起发送给客户端
list_for_each_entry(pos,&(myhead->mypoint),mypoint)
{
sprintf(allmsg+strlen(allmsg),"%s@%hu@",pos->ip,pos->someport);
}
//发送出去
write(p->sock,allmsg,strlen(allmsg));
continue;
}
//切割字符串,判断究竟是群发还是点播
char *q=strtok(buf,":"); //all:hello或者20000:hello
strcpy(msg,strtok(NULL,":"));
if(strcmp(q,"all")==0) //群发
{
//遍历链表转发出去
list_for_each_entry(pos,&(myhead->mypoint),mypoint)
{
//排除自己
if(pos->sock!=p->sock)
write(pos->sock,msg,strlen(msg));
}
}
else //点播
{
//遍历链表找到要点播的对象
list_for_each_entry(pos,&(myhead->mypoint),mypoint)
{
if(pos->someport==atoi(q))
write(pos->sock,msg,strlen(msg));
}
}
}
pthread_exit(NULL);
}
int main()
{
signal(SIGINT,fun);
pthread_t id;
int tcpsock;
int newsock;
int ret;
char buf[100];
//初始化表头
myhead=init_list();
//定义线程属性变量并初始化
pthread_attr_init(&myattr);
//调用你想设置的属性对应的接口函数
pthread_attr_setdetachstate(&myattr,PTHREAD_CREATE_DETACHED);
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定服务器自己的ip地址
bindaddr.sin_port=htons(10086); //绑定服务器自己的端口号
struct sockaddr_in clientaddr;
bzero(&clientaddr,sizeof(clientaddr));
int addrsize=sizeof(clientaddr);
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
printf("旧的文件描述符:%dn",tcpsock);
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//监听
ret=listen(tcpsock,8);
if(ret==-1)
{
perror("监听失败!n");
return -1;
}
while(1)
{
//接收客户端的连接请求
newsock=accept(tcpsock,(struct sockaddr *)&clientaddr,&addrsize);
if(newsock==-1)
{
perror("接收客户端的连接请求失败!n");
return -1;
}
printf("有新的客户端连接成功了,新的文件描述符:%dn",newsock);
printf("有新的客户端连接成功了,他的IP地址是:%sn",inet_ntoa(clientaddr.sin_addr));
printf("有新的客户端连接成功了,他的端口号是:%hun",ntohs(clientaddr.sin_port));
//把文件描述符存放到链表中
struct clientlist *newnode=malloc(sizeof(struct clientlist));
newnode->sock=newsock;
strcpy(newnode->ip,inet_ntoa(clientaddr.sin_addr));
newnode->someport=ntohs(clientaddr.sin_port);
INIT_LIST_HEAD(&(newnode->mypoint));
//插入到链表的尾部
list_add_tail(&(newnode->mypoint),&(myhead->mypoint));
//立马新建线程接收这个客户端发送的信息
pthread_create(&id,&myattr,routine,newnode);
}
}
#include "myhead.h"
int tcpsock;
void *routine(void *arg)
{
char buf[2048];
int ret;
while(1)
{
bzero(buf,2048);
ret=read(tcpsock,buf,2048);
if(ret==0) //说明服务器断开连接
{
printf("服务器挂了!n");
exit(-1); //结束整个进程
}
printf("服务器转发过来的信息是:%sn",buf);
}
}
int main()
{
pthread_t id;
int ret;
char buf[100];
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定客户端自己的ip地址
bindaddr.sin_port=htons(10000); //绑定客户端自己的端口号
//定义结构体变量存放对方(服务器)的ip和端口号
struct sockaddr_in serveraddr;
bzero(&serveraddr,sizeof(serveraddr));
serveraddr.sin_family=AF_INET;
serveraddr.sin_addr.s_addr=inet_addr("192.168.24.3"); //服务器的ip地址
serveraddr.sin_port=htons(10086); //服务器的端口号
//创建tcp套接字
tcpsock=socket(AF_INET,SOCK_STREAM,0);
if(tcpsock==-1)
{
perror("创建tcp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//连接服务器
ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
if(ret==-1)
{
perror("连接服务器!n");
return -1;
}
//创建线程专门接收服务器的信息
pthread_create(&id,NULL,routine,NULL);
//发送信息给服务器
while(1)
{
bzero(buf,100);
printf("请输入要发送给服务器的信息,按照如下格式输入!n");
printf("getlist 表示你想获取服务器上客户端的信息!n");
printf("all:真实的信息--》群发!n");
printf("端口号:真实的信息--》点播!n");
scanf("%s",buf);
write(tcpsock,buf,strlen(buf));
}
close(tcpsock);
return 0;
}
UDP发送接收
#include "myhead.h"
int main()
{
char buf[100];
char ip[16]={0};
int ret;
int udpsock;
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定自己的ip地址
bindaddr.sin_port=htons(20000); //绑定自己的端口号
struct sockaddr_in otheraddr;
bzero(&otheraddr,sizeof(otheraddr));
int size=sizeof(otheraddr);
//创建udp套接字
udpsock=socket(PF_INET,SOCK_DGRAM,0);
if(udpsock==-1)
{
perror("创建udp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(udpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(udpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//接收信息
bzero(buf,100);
recvfrom(udpsock,buf,100,0,(struct sockaddr *)&otheraddr,&size);
printf("ip %s 端口是 %hu 这个人发送的信息是:%sn",inet_ntop(AF_INET,&(otheraddr.sin_addr.s_addr),ip,sizeof(ip)),ntohs(otheraddr.sin_port),buf);
//回复I get
sendto(udpsock,"I get",strlen("I get"),0,(struct sockaddr *)&otheraddr,sizeof(otheraddr));
}
#include "myhead.h"
int main()
{
char buf[100];
int ret;
char ip[16]={0};
int udpsock;
//定义ipv4地址结构体变量
struct sockaddr_in bindaddr;
bzero(&bindaddr,sizeof(bindaddr));
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=htonl(INADDR_ANY); //绑定自己的ip地址
bindaddr.sin_port=htons(10086); //绑定自己的端口号
struct sockaddr_in otheraddr;
bzero(&otheraddr,sizeof(otheraddr));
otheraddr.sin_family=AF_INET;
otheraddr.sin_addr.s_addr=inet_addr("192.168.24.255"); //广播专用地址
otheraddr.sin_port=htons(20000); //对方的端口号
struct sockaddr_in addr;
bzero(&addr,sizeof(addr));
int size=sizeof(addr);
//创建udp套接字
udpsock=socket(PF_INET,SOCK_DGRAM,0);
if(udpsock==-1)
{
perror("创建udp套接字!n");
return -1;
}
//设置端口重复使用
int on=1;
setsockopt(udpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
//绑定ip和端口号
ret=bind(udpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
if(ret==-1)
{
perror("绑定失败!n");
return -1;
}
//设置套接字的属性为可以广播
setsockopt(udpsock,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
//发送广播信息
sendto(udpsock,"I am coming!",strlen("I am coming!"),0,(struct sockaddr *)&otheraddr,sizeof(otheraddr));
//接收信息
while(1)
{
bzero(buf,100);
recvfrom(udpsock,buf,100,0,(struct sockaddr *)&addr,&size);
printf("ip %s 端口是 %hu 回复了信息给我:%sn",inet_ntop(AF_INET,&(addr.sin_addr.s_addr),ip,sizeof(ip)),ntohs(addr.sin_port),buf);
}
return 0;
}
select设置超时
#include "myhead.h"
int main()
{
int ret;
//定义超时时间
struct timeval mytime;
bzero(&mytime,sizeof(mytime));
mytime.tv_sec=5; //5秒超时时间
//定义文件描述符集合变量
fd_set myset;
//添加要监测的文件描述符
FD_ZERO(&myset);
FD_SET(0,&myset);
printf("阻塞在select!n");
//调用select去监测 电子警察
ret=select(0+1,&myset,NULL,NULL,&mytime);
if(ret>0) //监测成功,说明有文件描述符发生了状态变化
{
//进一步去判断究竟是哪个文件描述符发生了状态改变
if(FD_ISSET(0,&myset))
{
printf("相信我,键盘一定有输入,不骗你的!n");
}
}
else if(ret==0)
{
printf("恭喜你,5秒到了,不给你输入!n");
return -1;
}
else
{
perror("select监测失败!n");
return -1;
}
return 0;
}



