一,socket编程
在TCP/IP协议中,IP地址+TCP/UDP的端口号,唯一地标识了网络通讯中的一个进程,“IP地址+端口号”就称为socket。
二,TCP_Server
2.1 基于多线程
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
static void userHelp(const char* str)
{
printf("%s [local_ip] [local_port]",str);
}
int startup(const char* _ip,int _port)
{
//socket打开一个 网络通讯端口,其中AF_INET:表示IPV4,SOCK_STREAM:表示面向流的传输,
//protocol参数默认选择为0
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock==-1)
{
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(_ip);
//bind的作用是将参数sock与local绑定在一起
//使sock这个文件描述符监听local所描述的地址与端口号
//成功返回0,失败返回-1
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) //success zero is return
{
perror("bind()");
exit(-2);
}
//listen声明sock处于监听状态,并且最多允许5个客户端处于连接等待状态
//如果收到更多的请求便忽略
if(listen(sock,5)==-1) //add listener
{
perror("listen()");
exit(-3);
}
return sock;
}
void* handlerRequest(void *arg)
{
int new_fd=(int)arg;
printf("new_fd=%d\n",new_fd);
while(1)
{
char buffer[1024];
ssize_t s=read(new_fd,buffer,sizeof(buffer));
if(s==-1)
{
perror("read");
}
if(s>0)
{
buffer[s]='\0';
printf("client:%s\n",buffer);
write(new_fd,buffer,strlen(buffer));
}
else
{
printf("read done...break\n");
break;
}
}
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
userHelp(argv[0]);
return 1;
}
int listen_sock=startup(argv[1],atoi(argv[2]));
while(1)
{
struct sockaddr_in client;
socklen_t len=sizeof(client);
//accept阻塞式等待,用来接收连接
int new_fd=accept(listen_sock,(struct sockaddr*)&client,&len);
if(new_fd<0)
{
perror("accept()");
continue;
}
printf("get a new client,%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
pthread_t id;
pthread_create(&id,NULL,handlerRequest,(void*)new_fd);
pthread_detach(id);
//return 0;
}
return 0;
}
2.2 基于多进程
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
static void userHelp(const char* str)
{
printf("%s [local_ip] [local_port]",str);
}
int startup(const char* _ip,int _port)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock==-1)
{
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(_ip);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) //success zero is return
{
perror("bind()");
exit(-2);
}
if(listen(sock,5)==-1) //add listener
{
perror("listen()");
exit(-3);
}
return sock;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
userHelp(argv[0]);
return 1;
}
int listen_sock=startup(argv[1],atoi(argv[2]));
while(1)
{
struct sockaddr_in client;
socklen_t len=sizeof(client);
int new_fd=accept(listen_sock,(struct sockaddr*)&client,&len);
if(new_fd<0)
{
perror("accept()");
continue;
}
//建立进程
pid_t id=fork();
if(id<0)
{
perror("fork");
close(new_fd);
}
else if(id==0)
{
close(listen_sock);
if(fork()>0)
{
close(new_fd);
exit(0);
}
while(1)
{
char buf[1024];
ssize_t s=read(new_fd,buf,sizeof(buf)-1);
if(s>0)
{
printf("get a new client,%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
buf[s]=0;
printf("client:%s\n",buf);
write(new_fd,buf,strlen(buf));
}
else
{
printf("read done...,break\n");
break;
}
}
close(new_fd);
}
}
return 0;
}
三,client
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
static void userHelp(char* str)
{
printf("%s [server_ip] [server_port]",str);
}
int main(int argc,char * argv[])
{
if(argc!=3)
{
userHelp(argv[0]);
return 1;
}
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("sock()");
exit(-1);
}
struct sockaddr_in client;
client.sin_family=AF_INET;
client.sin_port=htons(atoi(argv[2]));
client.sin_addr.s_addr=inet_addr(argv[1]);
if(connect(sock,(struct sockaddr*)&client,sizeof(client))<0)
{
perror("connect()");
exit(-2);
}
while(1)
{
char buffer[1024];
printf("please input:");
fflush(stdout);
ssize_t s=read(0,buffer,sizeof(buffer)-1);
if(s>0)
{
buffer[s-1]=0;
write(sock,buffer,strlen(buffer));
ssize_t _s=read(sock,buffer,sizeof(buffer)-1);
if(_s>0)
{
buffer[_s]=0;
printf("server echo#%s\n",buffer);
}
}
}
return 0;
}
四,测试结果(基于多进程版本)
clent:
server:
五,server,bind()失败原因调研
在建立连接后,我们发现如果单方面的结束掉server端,再在相同的端口运行server端程序将会出现以下问题:
这是因为,虽然server的应用程序终止了,但TCP的连接并没有完全断开,因此不能再次监听同样的server端口;我们用“netstat”命令查看一下:
我们发现8080端口还在被占用,这是因为TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL的时间后才能回到closed状态,所以当我们在连接中用“ctrl+c”结束掉server后,还需要进行等待,所以端口还是会处于监听状态。