关于套接字编程
套接字编程
IP地址+端口号就成为套接字
在TCP协议中。建立两个连接的进程个字有一个socket来标识,那么这两个socket组成desocketpair就表示一个唯一链接
socket用来描述网络连接一对一关系
socket地址的数据类型及相关函数
第一个是通用接口
第二个表示IPV4的地址使用socketaddr_in
第三个表示预间套接字
这样我们只需要知道某种socket结构体的首地址,就可以根据地质的具体类型字段确定结构体中的内容
服务器需要进行的操作
1>、服务器首先获得sockect调用socket函数
在这里我们用的是IPV4因此类型选择AF_INET。
由于是16位地址类型的AF_INET的所以我们采用sockaddr_in 类型的
客户端和服务端是一样的
sa_family:表示地址所属的类型 在这道理我们是AF_INET
_be16代表16位端口号
为什么是16位,因为在这里我们是从上往下进行交付
TCP的报头是16位端口号
server.sin_port=htons(atoi(port));
htons的功能:将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)
2>、接下来就应该绑定端口 调用bind函数绑定IP和端口号
3>、接下来服务器需要不断去监听有没有客户端发来请求调用listen函数
4>、接下来我们需要调用accept函数
accept()系统调用主要用在基于连接的套接字类型,比如SOCK_STREAM和SOCK_SEQPACKET。它提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符。新建立的套接字不在监听状态,原来所监听的套接字也不受该系统调用的影响。
5>、之后就可以进行一系列的读写操作了
2、客户端进行的操作就比较简单,他只需要和服务器进行连接
version1版本的server和client
server.c
#include
#include
#include
#include
#include
#include
#include
#include
int startup(char*ip,int port)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(2);
}
struct sockaddr_in server;
server.sin_family=AF_INET;
server.sin_port=htons(atoi(port));
server.sin_addr.s_addr=inet_addr(ip);
if(bind(sock,(struct sockaddr*)&server,sizeof(struct sockaddr_in))<0)
{
perror("bind");
exit(3);
}
if(listen(sock,10)<0)
{
perror("listen");
exit(4);
}
return sock;
}
void *usage(const char*proc)
{
printf("%s [local_ip] [local_port]\n",proc);
exit(5);
}
int main(int argv,char*argc[])
{
if(argv!=3)
{
usage(argc[0]);
exit(6);
}
int listen_sock=startup(argc[1],argc[2]);
printf("fd:%d\n",listen_sock);
while(1)
{
struct sockaddr_in client;
socklen_t len=sizeof(client);
int newsock=accept(listen_sock,(struct sockaddr*)&client,&len);
if(newsock<0)
{
perror("accept");
continue;
}
printf("get a client:[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
char buf[1024];
while(1)
{
ssize_t ret=read(newsock,buf,sizeof(buf)-1);
if(ret>0)
{
buf[ret]=0;
printf("client say:%s\n",buf);
write(newsock,buf,sizeof(buf)-1);
}
else if(ret==0)
{
printf("client quit!\n");
break;
}
else
{
perror("read");
exit(7);
}
}
close(newsock);
}
close(listen_sock);
return 0;
}
client.c
#include
#include
#include
#include
#include
#include
#include
#include
void Usage(char*proc)
{
printf("%s [server_ip] [server_port]\n",proc);
return 1;
}
int main(int argv,char*argc[])
{
if(argv!=3)
{
Usage(argc[0]);
return 2;
}
int sock=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in server_sock;
server_sock.sin_family=AF_INET;
server_sock.sin_port=htons(atoi(argc[2]));
server_sock.sin_addr.s_addr=inet_addr(argc[1]);
if(connect(sock,(struct sockaddr*)(&server_sock),sizeof(server_sock))<0)
{
perror("connect");
return 3;
}
while(1)
{
printf("please Enter:\n");
fflush(stdout);
char buf[1024];
ssize_t ret=read(0,buf,sizeof(buf)-1);
if(ret>0)
{
buf[ret-1]=0;
write(sock,buf,sizeof(buf)-1);
read(sock,buf,sizeof(buf)-1);
printf("server say:%s\n",buf);
}
}
close(sock);
return 3;
}
多进程版本的:
**server.c**
#include
#include
#include
#include
#include
#include
#include
#include
int startup(char*ip, int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock<0)
{
perror("socket");
exit(2);
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(port));
server.sin_addr.s_addr = inet_addr(ip);
if (bind(sock, (struct sockaddr*)&server, sizeof(struct sockaddr_in))<0)
{
perror("bind");
exit(3);
}
if (listen(sock, 10)<0)
{
perror("listen");
exit(4);
}
return sock;
}
void *usage(const char*proc)
{
printf("%s [local_ip] [local_port]\n", proc);
exit(5);
}
int main(int argv, char*argc[])
{
if(argv!=3)
{
Usage(argc[0]);
}
int listen_sock = startup(argc[1], argc[2]);
printf("fd:%d\n", listen_sock);
while (1)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int newsock = accept(listen_sock, (struct sockaddr*)&client, &len);
if (newsock<0)
{
perror("accept");
continue;
}
printf("get a client:[%s:%d]\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
pid_t id = fork();
if (id == 0)
{
close(listen_sock);
if (fork() == 0)
{
char buf[1024];
while (1)
{
ssize_t ret = read(newsock, buf, sizeof(buf)-1);
if (ret>0)
{
buf[ret] = 0;
printf("client say:%s\n", buf);
write(newsock, buf, sizeof(buf)-1);
}
else if (ret == 0)
{
printf("client quit!\n");
close(newsock);
exit(7);
}
else
{
perror("read");
exit(8);
}
}
}
else
{
exit(9);
}
}
else if (id>0)
{
close(newsock);
while((waitpid(-1,NULL,WNOHANG)>0));
continue;
}
else
{
perror("fork");
exit(9);
}
}
close(listen_sock);
return 0;
}
多线程版本的:
#include
#include
#include
#include
#include
#include
#include
#include
#include
int startup(char*ip, int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock<0)
{
perror("socket");
exit(2);
}
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(port));
server.sin_addr.s_addr = inet_addr(ip);
if (bind(sock, (struct sockaddr*)&server, sizeof(struct sockaddr_in))<0)
{
perror("bind");
exit(3);
}
if (listen(sock, 10)<0)
{
perror("listen");
exit(4);
}
return sock;
}
void *Usage(const char*proc)
{
printf("%s [local_ip] [local_port]\n", proc);
exit(5);
}
void* thread_handler(void*arg)
{
int newsock=*((int*)arg);
char buf[1024];
while (1)
{
ssize_t ret = read(newsock, buf, sizeof(buf)-1);
if (ret>0)
{
buf[ret] = 0;
printf("client say:%s\n", buf);
write(newsock, buf, sizeof(buf)-1);
}
else if (ret == 0)
{
printf("client quit!\n");
close(newsock);
exit(7);
}
else
{
perror("read");
exit(8);
}
}
}
int main(int argv, char*argc[])
{
if(argv!=3)
{
Usage(argc[0]);
}
int listen_sock = startup(argc[1], argc[2]);
printf("fd:%d\n", listen_sock);
while (1)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
int newsock = accept(listen_sock, (struct sockaddr*)&client, &len);
if (newsock<0)
{
perror("accept");
continue;
}
printf("get a client:[%s:%d]\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
pthread_t tid;
int ret=pthread_create(&tid,NULL,thread_handler,&newsock);
if(ret<0)
{
perror("pthread_create");
exit(9);
}
pthread_detach(tid);
}
close(listen_sock);
return 0;
}
为什么会出现server bind失败
看下图:
由于我们的客户端还没有退出,服务器就先断开连接了造成了这种情况,为什么会出现这种现象呢?
通信双方建立TCP连接后,主动关闭连接的一方就会进入TIME_WAIT状态
TCP是全双工的通信,它是提供可靠传输的,它有着三次握手和四次挥手,在四次挥手的过程中,主动断开连接的一方会进入TIME_WAIT状态。如果服务器端先断开连接那么就会进入TIME_WAIT状态。为什么需要TIME_WAIT?TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证。