一、字节序
1.字节序:指多字节数据在计算机内存中存储或者网络传输时个字节的存储顺序
2.Little endian 小端字节序
3.Big endian 大端字节序
4.网络字节序=大端字节序
5.例子:在内存中双字0x01020304(DWORD)的存储方式(来源百度百科)
4000&4001&4002&4003
LE 04 03 02 01
BE 01 02 03 04
6.x86系列CPU都是little-endian的字节序.
7.字节序转换相关API
#include <netinet/in.h>
unit16_t htons(unit16_t host16itvalue);//返回网络字节序=大端字节序
unit32_t htonl(unit32_t host32itvalue);//返回网络字节序=大端字节序
unit16_t ntons(unit16_t net16itvalue);//返回主机字节序的值unit32_t
ntohl(unit32_t net32itvalue);//返回主机字节序
- 补充:h代表host,n代表net,s代表short(2个字节),l代表long(4个字节),通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。有时可以用NADDR_ANY,INADDR_ANY指定地址让操作系统自己获取
二、TCP/UDP对比
1.TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,及发送数据之前,不需要建立连接
2.TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按次序达到;UDP是尽最大的努力交付,及不可靠交付
3.TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视屏会议等)
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5.TCP首部开销20字节;UDP的首部开销小,只有8个字节
6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
三、socket编程步骤
1.服务端
- 创建套接字
- 为套架子添加信息(IP地址和端口号)
- 监听网络连接
- 监听到到有客户端接入,接受一个连接
- 数据交互
- 关闭套接字,断开连接
2.客户端
- 创建套接字
- connect连接
- 数据交互
- 关闭套接字,断开连接
四、相关API简述
1.创建套接字
int socket(int domain,int type,int protocol);
domain:指明所使用的协议族,通常为 AF_INET,表示互联网协议族(TCP/IP)协议族
- AF_INET IPv4 因特网域(常用)
- AF_INET6 IPv6 因特网域
- AF_UNIX Unix域UDP
- AF_ROUTE 路由套接字
- AF_KEY 秘钥套接字
type:参数指定 socket的类型
- SOCK_STREAM:流式套接字提供可靠的,面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性;
- SOCK_DGRAM:数据报套接字定义了一种无连接的服,数据通过相互独立法人报文进行传输,是无序的,并且不保证是可靠的,无差错的。它使用数据报协议UDP;
- SOCK_RAW:允许程序底层协议,原始套接字允许对底层协议如IP或ICMP进行访问,功能强大但使用较为不方便,主要用于一些协议的开发。
protocol:通常赋值为0
- 0选择type类型对应的默认协议
- IPPROTO_TCP TCP传输协议
- IPPROTO_UDP UDP传输协议
- IPPROTO_SCTP SCTP传输协议
- IPPROTO_TIPC TIPC传输协议
2.添加IP地址和端口号
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//绑定IP地址和端口号到sockfd
- sockfd socket描述符
- addr 只一个指向包含本机IP地址及端口号等信息的sockaddr类型的指针,指向需要绑定给sockfd的协议地址结构,这个结构根据地址创建socket时的地址协议族的不同而不同
- IPV4对应的结构体:
struct sockaddr_in {
sa_family_t sin_family;//协议族
in_port_t sin_port;//端口号
struct in_addr sin_addr;//IP地址结构体
};
- 地址转换API
int inet_aton(const char* straddr,struct in_addr *addrp);//把字符串形式的“192.168.134.123”转化为网络能识别的格式
char* inet_ntpa(struct in_addr inaddr);//把网络格式的ip地址转化为字符串形式
3.监听网络连接
int listen(int sockfd, int backlog);//监听设置函数
- sockfd 是socket系统调用返回的服务器端socket描述符
- balcklog 指定在请求队列中允许的最大请求数
4.accept监听到有客户端接入,接受一个连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//该函数由TCP服务器调用,用于从已完成连接队列列头返回下一个已完成的连接。如果已经完成的连接队列为空,那么进程被投入睡眠
- sockfd 是socket系统调用返回的服务器端socket描述符
- addr 用来返回已连接的对端(客户端)的协议地址
- addrlen 客户端地址长度
- 返回值:一个新的套接字描述符,返回值表示已连接的套接字描述符。当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭
5.数据的收发
- 第一套:
ssize_t read(int fd, void *buf, size_t count);//接受
ssize_t write(int fd, const void *buf, size_t count);//发送
- 第二套//与上面功能几乎一样,建议用第二套的,更加直观一些
ssize_t send(int sockfd, const void *buf, size_t len, int flags);//发送数据
ssize_t recv(int sockfd, void *buf, size_t len, int flags);//接受数据
6.客户端 connect函数
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//客户机连接主机
- sockfd 是目的服务端socket描述符
- addr 是服务器端的IP地址和端口号的地址结构指针
- addrlen 地址长度通常被设置为sizeof(struct sickaddr);
- 返回值:成功0,反之-1,并且errno中包含相应的错误码
五、socket服务端代码实现
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
char readBuf[128]={"\0"};
char* msg="I get your messag";
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//创建套接字
//int socket(int domain, int type, int protocol);//正确返回socket描述符,错误返回-1
int s_fd=socket(AF_INET,SOCK_STREAM,0);//AF_INET,因特网IPV4;SOCK_STREAM,流式套接字,使用TCP协议;0,选择type类型对应默认协议
if(s_fd == -1){//判断是否创建成功
perror("socket");
exit(-1);
}
//2.bind//为套接字添加信息(IP地址和端口号)
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//绑定IP地址和端口号
s_addr.sin_family=AF_INET;//协议族
s_addr.sin_port=htons(8989);//端口号
inet_aton("192.168.127.138",&s_addr.sin_addr);
int s_bind=bind(s_fd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in));
//3.listen//监听网络连接
//int listen(int sockfd, int backlog);
int s_listen=listen(s_fd,10);
//4.accept//监听到有客户端接入,接受一个连接
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int len=sizeof(struct sockaddr_in);
int c_fd=accept(s_fd,(struct sockaddr*)&s_addr,&len);
if(c_fd == -1){
perror("accept");
}
else{
printf("get connect%s\n",inet_ntoa(c_addr.sin_addr));
}
//5.read//读取数据
//ssize_t read(int fd, void *buf, size_t count);
int n_read=read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}
else{
printf("get massege:%d,%s\n",n_read,readBuf);
}
//6.write//发送数据
// ssize_t write(int fd, const void *buf, size_t count);
memset(readBuf,'\0',128);
int n_write=write(c_fd,msg,strlen(msg));
return 0;
}
六、socket客户端代码实现
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(){
char readBuf[128]={"\0"};
char* msg="massege from clien";
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
int 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(8989);//端口号,htons();//返回网络字节序值
inet_aton("192.168.127.138",&c_addr.sin_addr);//inet_aton();//把字符串形式的“192.168.127.139”转化为网络能识别的格式
//2.connet
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int con=connect(c_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr));
if(con == -1){
perror("connect");
exit(-1);
}
//3.send
int n_write=write(c_fd,msg,strlen(msg));
//4.read
int n_read=read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}
else{
printf("massege from server:%d,%s\n",n_read,readBuf);
}
return 0;
}
七、实现双方聊天
1.服务端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.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;
int n_write;
char readBuf[128]={"\0"};
//char* msg="I get your messag";
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//创建套接字
//int socket(int domain, int type, int protocol);//正确返回socket描述符,错误返回-1
int s_fd=socket(AF_INET,SOCK_STREAM,0);//AF_INET,因特网IPV4;SOCK_STREAM,流式套接字,使用TCP协议;0,选择type类型对应默认协议
if(s_fd == -1){//判断是否创建成功
perror("socket");
exit(-1);
}
//2.bind//为套接字添加信息(IP地址和端口号)
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);//绑定IP地址和端口号
s_addr.sin_family=AF_INET;//协议族
s_addr.sin_port=htons(atoi(argv[2]));//端口号
inet_aton(argv[1],&s_addr.sin_addr);
int s_bind=bind(s_fd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in));
//3.listen//监听网络连接
//int listen(int sockfd, int backlog);
int s_listen=listen(s_fd,10);
//4.accept//监听到有客户端接入,接受一个连接
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int len=sizeof(struct sockaddr_in);
while(1){
c_fd=accept(s_fd,(struct sockaddr*)&s_addr,&len);
if(c_fd == -1){
perror("accept");
}
else{
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:");
scanf("%s",msg);
n_write=write(c_fd,msg,strlen(msg));
}
}
//5.read//读取数据
//ssize_t read(int fd, void *buf, size_t count);
while(1){
memset(readBuf,'\0',strlen(readBuf));
n_read=read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}
else{
printf("get massege:%d,%s\n",n_read,readBuf);
}
//6.write//发送数据
// ssize_t write(int fd, const void *buf, size_t count);
//n_write=write(c_fd,msg,strlen(msg));
}
break;
}
}
return 0;
}
2.客户端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.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 n_read;
int n_write;
char readBuf[128]={"\0"};
//char* msg="massege from client";
char msg[128]={"\0"};
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
//1.socket
int 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]));//端口号,htons();//返回网络字节序值
inet_aton(argv[1],&c_addr.sin_addr);//inet_aton();//把字符串形式的“192.168.127.139”转化为网络能识别的格式
//2.connet
//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int con=connect(c_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr));
if(con == -1){
perror("connect");
exit(-1);
}
while(1){
if(fork()==0){
while(1){
//3.send
memset(msg,0,sizeof(msg));
printf("input:");
scanf("%s",msg);
if(msg == 0){
exit(-1);
}
n_write=write(c_fd,msg,strlen(msg));
}
}
while(1){
//4.read
memset(readBuf,'\0',sizeof(msg));
n_read=read(c_fd,readBuf,128);
if(n_read == -1){
perror("read");
}
else{
printf("massege from server:%d,%s\n",n_read,readBuf);
}
}
}
return 0;
}