套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元,也就是要操作TCP/IP不能直接操作它,只能通过socket来进行操作,socket可以理解为是TCP/IP的封装。它包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远程主机的IP地址,远程进程的协议端口(也就是在socket连接bind那一步需要用的)。
socket之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认
端口号作用:一台拥有IP地址的主机可以提供很多服务,比如Web服务、FTP服务等,这些服务可以通过一个IP地址来实现,主机就是通过IP地址+端口号来区分不同的服务
字节序:字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序
? ? ? ? ? ? ? 小端字节序:将低序字节存储在起始地址
? ? ? ? ? ? ? 大端字节序:将高序字节存储在起始地址 ?(网络字节序 = 大端字节序)
Sockt服务器和客户端的开发步骤
(1)使用socket创建套接字
(2)使用bind为套接字添加信息(IP地址和端口号)
(3)使用listen监听网络连接
(4)当监听到有客户端接入,使用accept接受一个连接
(5)使用read和write进行数据交互
(6)关闭套接字,断开连接
*客户端只需要在服务器开始监听网络连接后使用connect接入,待服务器接受,即可进行读写,最后再close
服务器端?
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int s_fd;
//1.socket
s_fd = socket(AF_INET,SOCK_STREAM,0); //返回一个唯一标识socket的标识符,这两个宏在头文件中被包含了
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
//2.bind(不使用struct sockaddr这个结构体,一般使用struct sockaddr_in)
struct sockaddr_in s_addr;
s_addr.sin_family = AF_INET; //IPv4
s_addr.sin_port = htons(8989); //htons函数将主机字节序(小端存储)转为网络字节序(大端存储)
//int inet_aton(const char *cp, struct in_addr *inp);
//函数的第二个参数是一个结构体指针,sin_addr也是一个结构体变量,结构体里面还有内容,但是直接&s_addr.sin_addr
//sockaddr_in这个结构体里面的sin_addr就是struct in_addr这个结构体类型的变量,inp保存的就是sin_addr的地址,所以直接取地址
inet_aton("192.168.106.128",&s_addr.sin_addr); //IP地址转换,这是服务器的IP地址
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
//bind第二个参数的强转:&s_addr这样取得就是地址,也就是指针变量addr保存的,再直接(struct sockaddr *)强转就可以
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen
listen(s_fd,10);
//4.accept
//成功:返回一个新的socket描述符,用于后续的操作,因为s_fd要用于别的客户端的连接
int c_fd = accept(s_fd,NULL,NULL);//阻塞函数,如果没有客户端的连接请求,则会阻塞等待
//5.read
//6.write
printf("connect\n");
while(1);
return 0;
}
在电脑cmd框:输入telnet 192.168.10.67?8989 查看是否连接上服务器
telnet就是连接服务器的
服务器端
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int s_fd;
struct sockaddr_in s_addr; //服务器端
struct sockaddr_in c_addr; //客户端
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
//2.bind
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8989);
inet_aton("192.168.10.67",&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen
listen(s_fd,10);
//4.accept
int c_len = sizeof(struct sockaddr_in);
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
if(c_fd == -1)
{
perror("accept");
}
//char *inet_ntoa(struct in_addr in);
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr)); //打印的是客户端的IP地址,sin_addr保存的就是IP地址,
//而accept的第二个参数用于存放客户端的连接信息,是用户的IP地址
//5.read
//6.write
printf("connect\n");
while(1);
return 0;
}
在电脑cmd框:输入telnet 192.168.10.67?8989 查看是否连接上服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
int s_fd;
int n_read;
char readBuf[128] = {0};
char *msg = "wwjhaoshuai";
struct sockaddr_in s_addr; //服务器端
struct sockaddr_in c_addr; //客户端
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
//2.bind
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8989);
inet_aton("192.168.10.67",&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen
listen(s_fd,10);
//4.accept
int c_len = sizeof(struct sockaddr_in);
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
if(c_fd == -1)
{
perror("accept");
}
//char *inet_ntoa(struct in_addr in);
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr)); //打印的是客户端的IP地址
//5.read
n_read = read(c_fd,readBuf,128);
if(n_read == -1)
{
perror("read");
}
else
{
printf("get message:%d,connect:%s\n",n_read,readBuf);
}
//6.write
write(c_fd,msg,strlen(msg));
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
int c_fd;
int n_read;
char readBuf[128] = {0};
char *msg = "msg from client";
struct sockaddr_in c_addr; //客户端
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1)
{
perror("socket");
exit(-1);
}
//2.connect
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(8989);
inet_aton("192.168.10.67",&c_addr.sin_addr);
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int n_con = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in));
if(n_con == -1)
{
perror("connect");
exit(-1);
}
//3.send
write(c_fd,msg,strlen(msg));
//4.read
n_read = read(c_fd,readBuf,128);
if(n_read == -1)
{
perror("read");
}
else
{
printf("get message :%d,%s\n",n_read,readBuf);
}
return 0;
}
server:
get connect:192.168.10.67
get message:15,connect:msg from client
client:get message :11,wwjhaoshuai
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int s_fd;
int n_read;
int c_fd;
char readBuf[128] = {0};
char *msg = "I get your message";
struct sockaddr_in s_addr; //服务器端
struct sockaddr_in c_addr; //客户端
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
//2.bind
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen
listen(s_fd,10);
//4.accept
int c_len = sizeof(struct sockaddr_in);
while(1)
{
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
if(c_fd == -1)
{
perror("accept");
}
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
if(fork() == 0) //这是fork的第一种用法,父进程调用fork,使子进程处理此请求,父进程则等待下一个服务请求到达
{
//5.read
n_read = read(c_fd,readBuf,128);
if(n_read == -1)
{
perror("read");
}
else
{
printf("get message:%d,connect:%s\n",n_read,readBuf);
}
//6.write
write(c_fd,msg,strlen(msg));
break;
}
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int c_fd;
int n_read;
char readBuf[128] = {0};
char *msg = "msg from client";
struct sockaddr_in c_addr; //客户端
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1)
{
perror("socket");
exit(-1);
}
//2.connect
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int n_con = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in));
if(n_con == -1)
{
perror("connect");
exit(-1);
}
//3.send
write(c_fd,msg,strlen(msg));
//4.read
n_read = read(c_fd,readBuf,128);
if(n_read == -1)
{
perror("read");
}
else
{
printf("get message :%d,%s\n",n_read,readBuf);
}
return 0;
}
server:
./server 192.168.10.67 8989
get connect:192.168.10.67
get message:15,connect:msg from client
client:./client 192.168.10.67 8989
get message :18,I get your message
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int s_fd;
int n_read;
int c_fd;
if(argc != 3)
{
printf("param error\n");
exit(-1);
}
char readBuf[128] = {0};
char msg[128] = {0};
struct sockaddr_in s_addr; //服务器端
struct sockaddr_in c_addr; //客户端
memset(&s_addr,0,sizeof(struct sockaddr_in)); //把结构体的空间清零,以防其所占用的空间有垃圾值
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
//2.bind
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&s_addr.sin_addr);
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen
listen(s_fd,10);
//4.accept
int c_len = sizeof(struct sockaddr_in);
while(1)
{
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
if(c_fd == -1)
{
perror("accept");
}
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
if(fork() == 0) //父进程调用fork,使子进程处理此请求,父进程则等待下一个服务请求到达
{
if(fork() == 0) //要想使用两个while必须要使用线程或者子进程
{
while(1)
{
memset(msg,0,sizeof(msg));
printf("input: ");
scanf("%s",msg);
write(c_fd,msg,strlen(msg));
}
}
while(1)
{
memset(readBuf,0,sizeof(readBuf));
n_read = read(c_fd,readBuf,128);
if(n_read == -1)
{
perror("read");
}
else
{
printf("get message:%d,connect:%s\n",n_read,readBuf);
}
}
break;
}
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
int c_fd;
int n_read;
char readBuf[128] = {0};
if(argc != 3)
{
printf("param error\n");
exit(-1);
}
char msg[128] = {0};
struct sockaddr_in c_addr; //客户端
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
c_fd = socket(AF_INET,SOCK_STREAM,0);
if(c_fd == -1)
{
perror("socket");
exit(-1);
}
//2.connect
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int n_con = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in));
if(n_con == -1)
{
perror("connect");
exit(-1);
}
while(1)
{
if(fork() == 0) //同理
{
while(1)
{
memset(msg,0,sizeof(msg));
printf("input: ");
scanf("%s",msg);
write(c_fd,msg,strlen(msg));
}
}
while(1)
{
memset(readBuf,0,sizeof(readBuf));
n_read = read(c_fd,readBuf,128);
if(n_read == -1)
{
perror("read");
}
else
{
printf("get message :%d,%s\n",n_read,readBuf);
}
}
}
return 0;
}
创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。
socket则是对TCP/IP协议的封装和应用(程序员层面上)。也可以说,TPC/IP协议是传输层协议,主要解决数据 如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。
关于TCP/IP和HTTP协议的关系,网络有一段比较容易理解的介绍:“我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP、FTPTELNET等,也可以自己定义应用层协议。WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上。”
我们平时说的最多的socket是什么呢,实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。 实际上,Socket跟TCP/IP协议没有必然的联系。Socket编程接口在设计的时候,就希望也能适应其他的网络协议。
所以说,Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、connect、accept、send、read和write等等。
关于socket和TCP/IP协议关系的说法比较容易理解:
“TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。”?实际上,传输层的TCP是基于网络层的IP协议的,而应用层的HTTP协议又是基于传输层的TCP协议的,而Socket本身不算是协议,就像上面所说,它只是提供了一个针对TCP或者UDP编程的接口。socket是对端口通信开发的工具,它要更底层一些.
由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致 Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。
而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。
很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。
http协议是应用层的协义,有个比较形象的描述:HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。?两个计算机之间的交流无非是两个端口之间的数据通信,具体的数据会以什么样的形式展现是以不同的应用层协议来定义的,如HTTP、FTP