多线程并发TCP
客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#define PORT 8888
#define IP "192.168.10.10"
#define ERR_MSG(msg) do{\
fprintf(stderr,"line=%d",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
//创建套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd<0)
{
ERR_MSG("socket");
return -1;
}
printf("-流式套接字\n");
//允许端口号重复使用
int reuse = 1;
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("端口允许快速重用\n");
//创建一个地址信息结构体,bind绑定需要用到
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//连接服务器
if(connect(fd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("connect");
return -1;
}
printf("[%s:%d]连接服务器成功\n",IP,PORT);
//接收与发送
char buf[128];
ssize_t size;
while(1)
{
//发送
printf("客户端请输入>>>");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(send(fd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
return -1;
}
printf("发送成功\n");
//接收
bzero(buf,sizeof(buf));//清空数组内容
// size = read(fd,buf,sizeof(buf));
size = recv(fd,buf,sizeof(buf),0);
// size = recvfrom(fd,buf,sizeof(buf),0,NULL,NULL);
if(size<0)
{
ERR_MSG("recv");
return -1;
}else if(size==0)
{
printf("[%s:%d]服务器下线\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port));
break;
}
printf("[%s:%d] 接收成功%d:%s\n",IP,PORT,fd,buf);
}
close(fd);
return 0;
}
IO流中select
服务器端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/time.h>
#include <string.h>
#define PORT 8888 //1014~49151
#define IP "192.168.148.17"
#define ERR_MSG(msg) do{\
fprintf(stderr,"line:%d",__LINE__);\
perror(msg);\
}while(0)
int Keyboardvt(fd_set readfds); //键盘事件
int clientct(int psfd,int *pmaxfds,fd_set *preadfds,struct sockaddr_in psavecin[]); //客户端连接事件
int Interactionvt(int *pmaxfds,int i,struct sockaddr_in psavecin[],fd_set *preadfds); //客户端交互事件
int prepare(int *pmaxfds,fd_set *ptmpfds,int s_res); //检查文件描述符是否准备就绪
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("流式套接字创建成功 sfd=%d\n",sfd);
//允许端口快速被复用
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("允许端口被重复使用\n");
//填充服务器的地址信息结构体,给bind函数使用
//真实的地址信息结构体根据地址族制定,AF_LNET-->>man 7 ipp
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//绑定服务器的地址信息,必须绑定
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("bind");
return -1;
}
printf("绑定成功!\n");
//将套接字设置尾监听状态
if(listen(sfd,128)<0)
{
ERR_MSG("listen");
return -1;
}
printf("监听成功!\n");
//创建一个读集合
fd_set readfds,tmpfds;
FD_ZERO(&readfds); //清空集合
//将文件描述符表加入到集合
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
int s_res;
int newfds = -1;
int maxfds = sfd; //标记文件描述符中最大的一个存到max中
struct sockaddr_in savecin[1024];
while(1) //让内核监听集合中的文件描述符是否准备就绪
{
tmpfds = readfds; //把文件描述符表拷贝到tmp中,便于操作
prepare(&maxfds,&tmpfds,s_res);
printf("__%d__\n",__LINE__);
//判断那个文件描述符准备就绪了,就执行哪一个文件描述符
for(int i=0;i<=maxfds;i++)
{
if(FD_ISSET(i,&tmpfds)==0)
{
continue;
}
if(0==i)
{
printf("键盘事件被触发\n");
Keyboardvt(readfds);
}
else if(sfd == i)
{
printf("客户端连接被触发\n");
clientct(sfd,&maxfds,&readfds,savecin);
}
else
{
printf("触发客户端交互事件\n");
Interactionvt(&maxfds,i,savecin,&readfds);
}
}
}
close(sfd);
return 0;
}
int prepare(int *pmaxfds,fd_set *ptmpfds,int s_res)
{
s_res = select(*pmaxfds+1,ptmpfds,NULL,NULL,NULL);
if(s_res<0)
{
ERR_MSG("select");
return -1;
}
else if(s_res == 0)
{
printf("time out\n");
return 0;
}
return 0;
}
int Interactionvt(int *pmaxfds,int i,struct sockaddr_in psavecin[],fd_set *preadfds)
{
char buf[128];
ssize_t res;
bzero(buf,sizeof(buf));
//接收
res = recv(i,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("recv");
return -1;
}
else if(res == 0)
{
printf("[%s:%d]客户端下线\n %d",inet_ntoa(psavecin[i].sin_addr),ntohs(psavecin[i].sin_port),i);
close(i);
FD_CLR(i,preadfds); //把文件描述法冲readfds中删除
while(FD_ISSET(*pmaxfds,preadfds) == 0 && *pmaxfds-- > 0); //更新集合中的最大文件描述符表
return 0;
}
printf("[%s:%d] newfd=%d:%s\n",inet_ntoa(psavecin[i].sin_addr),ntohs(psavecin[i].sin_port),i,buf);
//发送
if(send(i,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
return -1;
}
printf("发送成功\n");
return 0;
}
int clientct(int psfd,int *pmaxfds,fd_set *preadfds,struct sockaddr_in psavecin[])
{
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
int newsfd = accept(psfd,(struct sockaddr*)&cin ,&addrlen);
if(newsfd<0)
{
ERR_MSG("accept");
return -1;
}
printf("[%s:%d] newfd=%d 客户端连接成功\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newsfd);
psavecin [newsfd] = cin;
FD_SET(newsfd,preadfds); //将newfa加入到readfds中,用于监听文件描述符
*pmaxfds = *pmaxfds>newsfd?*pmaxfds:newsfd; //判断目前最大的文件描述法并更新到max中
return 0;
}
int Keyboardvt(fd_set readfds)
{
int sndfd;
char buf[128];
int res = scanf("%d %s",&sndfd,buf);
while(getchar()!='\n');
if(res != 2)
{
printf("请输入正确格式:fd , string\n");
return -1;
}
if(sndfd<=3 || FD_ISSET(sndfd,&readfds) == 0)
{
printf("sndfd=%d错误,请输入合法文件描述符\n",sndfd);
return -1;
}
if(send(sndfd,buf,sizeof(buf),0)<0)
{
ERR_MSG("send");
return -1;
}
return 0;
}
客户端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#define IP "192.168.10.10"
#define PORT 8888
#define ERR_MSG do{\
fprintf(stderr,"__%d__\n",__LINE__);\
perror("msg");\
}while(0);
int main(int argc, const char *argv[])
{
//创建一个socket创建流失套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
ERR_MSG("socket");
return -1;
}
printf("套接字创建完成\n");
//绑定服务器的ip端口信息
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
/* if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)<0))//客户端可选择不用绑定
{
ERR_MSG("bind");
return -1;
}
printf("绑定成功\n");
*/
//连接服务器
if(connect(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
{
ERR_MSG("connect");
return -1;
}
printf("连接服务器成功\n");
fd_set readfds,tmpfds; //创建一个读集合
FD_ZERO(&readfds); //清空文件描述符
FD_SET(0,&readfds); //把文件描述符加入到集合中
FD_SET(sfd,&readfds);
int maxfd = sfd;
char buf[128],buf1[128];
ssize_t res1,res;
int s_res;
while(1)
{
//发送接收
tmpfds = readfds; //拷贝文教描述符便于操作
//让内核检验集合中那个文件描述符准备完成
s_res = select(maxfd+1,&tmpfds,NULL,NULL,NULL);
if(s_res<0)
{
ERR_MSG("select");
return -1;
}else if(s_res == 0)
{
printf("time out\n");
break;
}
//循环判断触发那个事件
if(FD_ISSET(0,&tmpfds))
{
// printf("发送信息>>>\n");
bzero(buf,sizeof(buf));
scanf("%s",buf);
printf("------发送信息成功----\n");
res = send(sfd,buf,sizeof(buf),0);
if(res<0)
{
ERR_MSG("send");
}
// printf("[%s:%d]客户端发:%s\n",IP,PORT,buf);
}
if(FD_ISSET(sfd,&readfds))
{
bzero(buf1,sizeof(buf1));
res1 = recv(sfd,buf1,sizeof(buf1),0);
if(res1<0)
{
ERR_MSG("recv");
return -1;
}
else if(res1 ==0)
{
printf("[%s;%d]服务器下线 sfd: %d\n",IP,PORT,sfd);
break;
}
printf("[%s:%d]服务器发:%s sfd=%d\n",IP,PORT,buf1,sfd);
}
}
close(sfd);
return 0;
}
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 多线程的并发TCP通信,select的服务器及客户端
发表评论 取消回复