接口的使用
接口:connect、listen、accept、send和recv
服务端没有开始listen客户端是无法connect上的
可以使用setsockopt来修改sock属性,使之可以忽视上次tcp请求结束time_wait时间来再次建立相同端口的链接
利用tcp进行设计简易的聊天窗口
客户端:
#include <23c.h>
int main(int argc,char*argv[])
{
//./client.c 192.168.154.128 1234
ARGS_CHECK(argc,3);
int sockfd=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in serverAddr;
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr(argv[1]);
serverAddr.sin_port=htons(atoi(argv[2]));
//connect
int ret=connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
ERROR_CHECK(ret,-1,"connect");
//实现即时聊天
fd_set rdset;
char buf[4096]={0};
while(1)
{
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO,&rdset);
FD_SET(sockfd,&rdset);
select(sockfd+1,&rdset,NULL,NULL,NULL);
if(FD_ISSET(STDIN_FILENO,&rdset))
{
bzero(buf,sizeof(buf));
int ret=read(STDIN_FILENO,buf,sizeof(buf));
ERROR_CHECK(ret,-1,"read");
if(ret == 0)
{
puts("I quit!");
break;
}
ret =send(sockfd,buf,strlen(buf),0);
ERROR_CHECK(ret,-1,"send");
}
if(FD_ISSET(sockfd,&rdset))
{
bzero(buf,sizeof(buf));
int ret=recv(sockfd,buf,sizeof(buf),0);
ERROR_CHECK(ret,-1,"recv");
if(ret == 0)
{
printf("hehe\n");
break;
}
printf("azhen: %s\n",buf);
}
}
close(sockfd);
return 0;
}
服务端:
#include <23c.h>
int main(int argc,char*argv[])
{
//./server.c 192.168.154.128 1234
ARGS_CHECK(argc,3);
int sockfd=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in serverAddr;
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr(argv[1]);
serverAddr.sin_port=htons(atoi(argv[2]));
int optval=1;
int ret=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(int));
ERROR_CHECK(ret,-1,"setsockopt");
ret=bind(sockfd,(struct sockaddr*)&serverAddr,sizeof(serverAddr));
ERROR_CHECK(ret,-1,"bind");
listen(sockfd,10);
struct sockaddr_in clientAddr;
socklen_t clientAddrlen=sizeof(clientAddr);
int netfd=accept(sockfd,(struct sockaddr *)&clientAddr,&clientAddrlen);
//printf("client addr len=%d\n",clientAddrlen);
//printf("client ip=%s\n",inet_ntoa(clientAddr.sin_addr));
//printf("client port=%d\n",ntohs(clientAddr.sin_port));
//实现即时聊天
fd_set rdset;
char buf[4096]={0};
while(1)
{
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO,&rdset);
FD_SET(netfd,&rdset);
select(netfd+1,&rdset,NULL,NULL,NULL);
if(FD_ISSET(STDIN_FILENO,&rdset))
{
bzero(buf,sizeof(buf));
int ret=read(STDIN_FILENO,buf,sizeof(buf));
ERROR_CHECK(ret,-1,"read");
if(ret == 0)
{
send(netfd,"nishigehaoren!",14,0);
break;
}
ret =send(netfd,buf,strlen(buf),0);
ERROR_CHECK(ret,-1,"send");
}
if(FD_ISSET(netfd,&rdset))
{
bzero(buf,sizeof(buf));
int ret=recv(netfd,buf,sizeof(buf),0);
ERROR_CHECK(ret,-1,"recv");
if(ret == 0)
{
printf("quit!\n");
break;
}
printf("aqiang: %s\n",buf);
}
}
close(netfd);
close(sockfd);
printf("end\n");
return 0;
}
注意事项:可以把tcp传输看成全双工管道,这是就要注意有一方进程退出后,另一方会无限制打印空,是因为写端关闭,客户端read读到EOF(0)是会一直就绪,select就不会阻塞,导致不断打印。
这时候解决办法是加判断