目录
一、相关概念
TCP与UDP的区别:
端口号作用
字节序
二、Socket服务器与客户端的开发步骤
三、API
四、实现双方聊天
五、实现多方聊天
地址:IP地址和端口号
数据(数据格式):协议(HTTP/TCP/UDP)
socket(套接字):TCP/UDP
①TCP面向连接(如打电话之前先拨号建立连接);UDP是无连接的,即发送数据之前,不需要建立连接。
②TCP提供可靠服务。通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,不保证可靠交付。
③ TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议)。
④每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一,多对多的交互通信。
⑤TCP首部开销20字节;UDP首部开销小,只有8字节。
⑥TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。
一台拥有IP地址的主机可以提供许多服务,比如Web服务,FTP服务,SMTP服务等。主机是通过“IP地址+端口号”来区分不同的服务的
字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
常见序:
Little endian:小端字节序,将低序字节存储在起始地址
Big endian:大端字节序,将高序字节存储在起始地址
网络字节序=大端字节序
例:0x01020304放入4000-4003与0x1234abcd放入0x0000-0x0003的存储
地址 | Big endian | Little endian | 地址 | Big endian | Little endian | |
4000 | 0x01 | 0x04 | 0x0000 | 0x12 | 0xcd | |
4001 | 0x02 | 0x03 | 0x0001 | 0x34 | 0xab | |
4002 | 0x03 | 0x02 | 0x0002 | 0xab | 0x34 | |
4003 | 0x04 | 0x01 | 0x0003 | 0xcd | 0x12 |
1、连接协议(TCP/UDP)
int socket(int domain,int type,int protocol);
返回值:socket描述符 sockfd
参数说明:
①domain:指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)
- AF_INET IPv4因特网域
- AF_INET6 IPv6因特网域
- AF_UNIX Unix域
- AF_ROUTE 路由套接字
- AF_KEY 密钥套接字
- AF_UNSPEC 未指定
②type:指定socket的类型
- SOCK_STREAM:流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性
- SOCK_DGRAM:数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP。
- SOCK_RAW:允许程序使用低层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
③protocol:通常赋值‘0’
- 0 选择type类型对应的默认协议
- IPPROTO_TCP TCP传输协议
- IPPROTO_TCP UDP传输协议
- IPPROTO_SCTP SCTP传输协议
- IPPROTO_TIPC TIPC传输协议
2、为套接字添加信息
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
用于绑定IP地址和端口号到sockfd
参数说明:
①sockfd:socket描述符
②addr:一个指向包含有本机IP地址和端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同
例IPv4:
struct sockaddr{ unsigned short sa_family; //协议族 char sa_data[14]; //IP+端口号 }; 同等替换(此种常用): struct sockaddr_in{ sa_family_t sin_family; //协议族 in_port_t sin_port; //端口号 struct in_addr sin_addr; //IP地址结构体 unsigned char sin_zero[8]; //填充,无实际意义,为和sockaddr结构在内存中对齐,这样才能转换 }
3、地址转化API
把字符串形式的“192.168.1.1”转换为网络能识别的格式:
int inet_aton(const char* straddr,struct in_addr* addrp);
把网络格式的ip地址转换为字符串格式:
char* inet_ntoa(struct in_addr inaddr);
4、监听
int listen(int sockfd, int backlog);
参数说明:
①sockfd:socket描述符
②backlog:指定在请求队列中允许的最大请求数
5、连接
int accept(int aockfd, struct sockaddr *addr, socklen_t *addrlen);
参数说明:
①sockfd:socket描述符
②addr:客户端的协议地址
③addrled:客户端地址长度
返回值:
返回已连接的套接字描述符
6、数据收发
收:
size_t send(int s, const void *msg.size_t len, int flags);
参数说明:
①s:套接字
②msg:待发数据
③len:数据长度
④flags:控制选项,一般设置为0
发:
size_t recv(int s, void *buf.size_t len, int flags);
参数说明:
①s:套接字
②buf:接收缓冲区
③len:数据长度
④flags:控制选项,一般设置为0
7、客户端连接函数
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数说明:
①sockfd:目的服务器的socket描述符
②addr:服务器端的IP地址和端口号的地址结构指针
③addrlen:地址长度,常被设置为sizeof(struct sockaddr)
返回值:
成功返回0,遇到错误时返回-1,并且error中包含相应的错误码
8、字节序转换API
#include
uint16_t htons(uint16_t host16bitvalue); //返回网络字节序的值
代码实现:
服务端:
#include
#include
#include
//#include
#include
#include
#include
#include
int main()
{
int s_fd;//socket标识符
int n_read;//接收的内容
char readbuf[128];//数据存放缓冲区
char *msg = "I get your connect\n";//回复内容
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
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8888);
inet_aton("192.168.2.3",&s_addr.sin_addr);//转换为网络格式
bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
//3.listen
listen(s_fd,10);
//4.accep
int c_len = sizeof(struct sockaddr_in);
int c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
if(c_fd == -1){
perror("accept");
}
printf("connect\n");
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
//5.read
n_read = read(c_fd,readbuf,128);
if(n_read == -1){
perror("read\n");
}else{
printf("get message:%d,%s\n",n_read,readbuf);
}
//6.recv
write(c_fd,msg,strlen(msg));
return 0;
}
①先用另一个终端中用telnet连接:
服务器:
connect
get connect:192.168.2.3
get message:5,www
另一个终端:
telnet 192.168.2.3 8888
Trying 192.168.2.3...
Connected to 192.168.2.3.
Escape character is '^]'.
www
I get your connect
Connection closed by foreign host.
客户端:
#include
#include
#include
//#include
#include
#include
#include
#include
int main()
{
int c_fd;
int n_read;
char readbuf[128];
char *msg = "msg from client\n";
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);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(8888);
inet_aton("192.168.2.3",&c_addr.sin_addr);
//2.connect
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -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\n");
}else{
printf("get message:%d,%s\n",n_read,readbuf);
}
return 0;
}
②服务器和客户端连接
服务器:
connect
get connect:192.168.2.3
get message:16,msg from client
客户端:
get message:19,I get your connect
服务器端:
#include
#include
#include
//#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int s_fd;
int c_fd;
int n_read;
char readbuf[128];
//char *msg = "I get your connect\n";
char msg[128] ={0};
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
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
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.accep
int c_len = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
if(c_fd == -1){
perror("accept");
}
printf("connect\n");
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
if(fork() == 0){
if(fork() == 0){
while(1){
memset(msg,0,sizeof(msg));
printf("input: ");
gets(msg);
write(c_fd,msg,strlen(msg));
}
}
//5.read
while(1){
memset(readbuf,0,sizeof(readbuf));
n_read = read(c_fd,readbuf,128);
if(n_read == -1){
perror("read\n");
}else{
printf("get message:%d,%s\n",n_read,readbuf);
}
}
//6.recv
break;
}
}
return 0;
}
客户端:
#include
#include
#include
//#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int c_fd;
int n_read;
char readbuf[128];
//char *msg = "msg from client\n";
char msg[128] = {0};
struct sockaddr_in c_addr;
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
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);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&c_addr.sin_addr);
//2.connect
if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
perror("connect");
exit(-1);
}
while(1){
if(fork() == 0){
while(1){
//3.send
memset(msg,0,sizeof(msg));
printf("input: ");
gets(msg);
write(c_fd,msg,strlen(msg));
}
}
while(1){
memset(readbuf,0,sizeof(readbuf));
//4.read
n_read = read(c_fd,readbuf,128);
if(n_read == -1){
perror("read\n");
}else{
printf("get message:%d,%s\n",n_read,readbuf);
}
}
}
return 0;
}
在服务器端加入mark:
#include
#include
#include
//#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int s_fd;
int c_fd;
int n_read;
int mark = 0;
char readbuf[128];
//char *msg = "I get your connect\n";
char msg[128] ={0};
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if(argc != 3){
printf("param is not good\n");
exit(-1);
}
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
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.accep
int c_len = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
if(c_fd == -1){
perror("accept");
}
mark++;
printf("connect\n");
printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
if(fork() == 0){
if(fork() == 0){
while(1){
sprintf(msg,"No.%d client",mark);
write(c_fd,msg,strlen(msg));
sleep(3);
}
}
//5.read
while(1){
memset(readbuf,0,sizeof(readbuf));
n_read = read(c_fd,readbuf,128);
if(n_read == -1){
perror("read\n");
}else{
printf("get message:%d,%s\n",n_read,readbuf);
}
}
//6.recv
break;
}
}
return 0;
}