tcp服务器(socket)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <wait.h>
void usage(char* proc)//此函数用来打印使用方法
{
printf("usage: %s [local_ip] [port]\n",proc);
}
int startup(const char *op,int port)//启动监听套接字
{
int sock = socket(AF_INET,SOCK_STREAM,0);//创建一个套接字
if(sock<0)//创建失败返回
{
perror("socket");
exit(-1);
}
struct sockaddr_in local;//定义一个ipv4的结构体
local.sin_family = AF_INET;
local.sin_port = htons(port);//端口
local.sin_addr.s_addr = inet_addr(op);//ip
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)//绑定套接字
{
perror("bind");
exit(-2);
}
if(listen(sock,5)<0)//开启监听
{
perror("listen");
exit(-3);
}
return sock;//返回绑定且监听的文件描述符
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int sock = startup(argv[1],atoi(argv[2]));
while(1)
{
//char buf[1024];
struct sockaddr_in client;//输出型结构体
socklen_t len = sizeof(client);
int client_sock;
if((client_sock = accept(sock,(struct sockaddr*)&client,&len)) < 0)
{
perror("accept");
exit(-4);
}
printf("get new client...\n");//连接成功
while(1)
{
char buf[1024];
size_t i = 0;
if((i=read(client_sock,buf,sizeof(buf)-1)) <= 0)
{
printf("read done...\n");
exit(-5);
}
else
{
buf[i]=0;
printf("client:%s",buf);
write(client_sock,buf,sizeof(buf));
}
}
//read(client_sock,buf,sizof(buf-1));
//printf("client:%s\n",buf);
close(client_sock);
}
close(sock);
return 0;
}
上面是一个简单的tcp服务器,但是一次只能连一个,也就是阻塞式等待,下面改进一下,编写一个多进程服务器。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <wait.h>
void usage(char* proc)
{
printf("usage: %s [local_ip] [port]\n",proc);
}
int startup(const char *op,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = inet_addr(op);
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
perror("bind");
exit(-2);
}
if(listen(sock,5)<0)
{
perror("listen");
exit(-3);
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int sock = startup(argv[1],atoi(argv[2]));
while(1)
{
//char buf[1024];
struct sockaddr_in client;
socklen_t len = sizeof(client);
//client.sin_family = AF_INET;
//client.sin_port = ntohs(atoi(argv[2]));
//client.sin_addr.s_addr = inet_ntoa();
int client_sock;
if((client_sock = accept(sock,(struct sockaddr*)&client,&len)) < 0)
{
perror("accept");
exit(-4);
}
printf("get new client...\n");
pid_t pid = fork();
if(pid < 0)
{
close(client_sock);
exit(-5);
}
else if(pid == 0)
{
close(sock);
if(fork() > 0)
{
exit(-1);
}
while(1)
{
char buf[1024];
size_t i = 0;
if((i=read(client_sock,buf,sizeof(buf)-1)) <= 0)
{
printf("read done...\n");
exit(-5);
}
else
{
buf[i]=0;
printf("client:%s",buf);
write(client_sock,buf,sizeof(buf));
}
}
}
else
{
close(client_sock);
}
/* while(1)
{
char buf[1024];
size_t i = 0;
if((i=read(client_sock,buf,sizeof(buf)-1)) <= 0)
{
printf("read done...\n");
exit(-5);
}
else
{
buf[i]=0;
printf("client:%s",buf);
write(client_sock,buf,sizeof(buf));
}
}*/
//read(client_sock,buf,sizof(buf-1));
//printf("client:%s\n",buf);
//close(client_sock);
}
//close(sock);
return 0;
}
多进程服务器编写完成,可是占用内存空间大,因此出现了多线程服务器,更加快速的访问服务器。
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <wait.h>
#include <pthread.h>
void usage(char* proc)
{
printf("usage: %s [local_ip] [port]\n",proc);
}
int startup(const char *op,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(-1);
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = inet_addr(op);
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
perror("bind");
exit(-2);
}
if(listen(sock,5)<0)
{
perror("listen");
exit(-3);
}
return sock;
}
void* huddler(void* arg)
{
int new_fd =(int)arg;
while(1)
{
char buf[1024];
size_t i = 0;
if((i=read(new_fd,buf,sizeof(buf)-1)) <= 0)
{
printf("read done...\n");
exit(-5);
}
else
{
buf[i]=0;
printf("client:%s",buf);
write(new_fd,buf,sizeof(buf));
}
}
}
int main(int argc,char* argv[])
{
if(argc != 3)
{
usage(argv[0]);
return 1;
}
int sock = startup(argv[1],atoi(argv[2]));
while(1)
{
//char buf[1024];
struct sockaddr_in client;
socklen_t len = sizeof(client);
//client.sin_family = AF_INET;
//client.sin_port = ntohs(atoi(argv[2]));
//client.sin_addr.s_addr = inet_ntoa();
int client_sock;
if((client_sock = accept(sock,(struct sockaddr*)&client,&len)) < 0)
{
perror("accept");
exit(-4);
}
printf("get new client...\n");
pthread_t id;
pthread_create(&id,NULL,huddler,(void*)client_sock);
pthread_detach(id);
}
//close(sock);
return 0;
}
以上就是简单的tcp服务器。
当一个客户向服务器或者服务器向客户发起关闭连接的请求时,会进入time_wait等待时间,这时服务器如果先关闭,无法立即从新绑定套接字,会停留在time_wait时间,等待丢失或者没有收到的数据,这就是所谓的四次握手最后一次。
TIME_WAIT状态存在的理由
TCP/IP协议就是这样设计的,是不可避免的。主要有两个原因:
1)可靠地实现TCP全双工连接的终止
TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK。如果A端不维持TIME_WAIT状态,而是处于CLOSED 状态,那么A端将响应RST分节,B端收到后将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。
因而,要实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,主动关闭连接的A端必须维持TIME_WAIT状态 。
2)允许老的重复分节在网络中消逝
TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的“新连接”,“前一个连接”的迷途重复分组在“前一个连接”终止后到达,而被“新连接”收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。