1.TCP/UDP
①TCP面向连接需要连接,而UDP不需要连接
②tcp可靠,传输数据无差错,不丢失,不重复,且按序到达,udp最大努力交付,即不保证可靠。
③tcp面向字节流,udp报文,udp没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对事实应用很有用,如IP电话,视频会议)
④tcp连接点到点,udp支持一对一,一对多,多对一,多对多的交互通信
⑤tcp首部开销20字节,udp8字节
⑥tcp逻辑通信信道是全双工的可靠信道,udp是不可靠的
2.端口号作用
①一台拥有IP地址的主机可以提供许多服务②IP地址与网络服务关系是一对多
③IP+端口号区分不同的服务
④服务器一般都是通过知名端口号来识别的。例:对于每个tcp/ip实现来说,FTP服务器tcp端口号是21。Linux应用层一般都是5000-10000之间
3.字节序
Little endian 小端字节序(低字节存储在起始地址)
Big endian 大端字节序
网络字节序=大端字节序
4.socket编程步骤
服务器
①创建套接字(通道)
socket()
②添加信息(IP,端口号)
bind()
③监听网络连接
listen()
④监听到客户端接入,接受
accept()
⑤数据交互
read()
write()
⑥关闭套接字,断开连接
close()
客户端
①发起连接
connect()
②数据交互
③断开连接
int socket(int domain,int type,int protocol)
返回值:网络描述符
参数1:所使用的协议族,通常使用AF_TNET,表示IPv4因特网域
参数2:指定socket类型
SOCK_STREAM:TCP
SOCK_DGRAM:UDP
SOCK_RAW:直接访问IP或ICMP,通常用于协议开发
参数3:通常0,选择tpye类型对应的默认协议
int bind(int sockfd,const struct sockaddr* addr,socklen_t addr len)
用于绑定IP地址和端口号到socketfd
参数1:网络描述符
参数2:包含IP和端口号的结构体指针
参数3:参数2占用字节个数sizeof
补充:
参数2结构体通常用struct sockaddr_in:
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
struct in_addr {
__be32 s_addr;
};
成员1:协议族
成员2:端口号(要转换网络字节序)
成员3:IP地址结构体
成员4:没有实际意义,只为跟sockaddr内存对齐,这样才能转换,不需要配置
int inet_aton(const char* straddr,struct in_addr* addrp)
把字符串形式192.168.1.111转换为网络格式
char* inet_ntoa(struct in_addr inaddr)
把网络形式的IP转换为字符串形式
注:头文件
int listen(int sockfd,int backlog)
设置处理最大连接数,只用于服务器端。
参数1:网络描述符
参数2:最大连接数
int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen)
返回值:新的套接字描述符,用于读写操作
参数1:网络描述符
参数2:客户端的地址结构体
参数3:参数2结构体的长度(变量int)
read()
write()
第二套API
ssize_t send(int s,const void* msg,ssize_t len,int flags)
最后一个参数设置阻塞,一般0
ssize_t recv(int s,void* buf,ssize_t len,int flags)
int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen)
返回值:失败返回-1,errno看错误代码
参数1:服务器端的网络描述符(socket)
参数2:服务端的IP和端口号结构体指针
参数3:参数2结构体的内存大小sizeof
返回网络字节序(大端字节序)
uint16_t htons(uint16_t host16bitvalue)
uint32_t htonl(uint32_t host32bitvalue)
返回主机字节序
uint16_t ntohs(uint16_t net16bitvalue)
uint32_t ntohl(uint32_t net32bitvalue)
h代表host,n代表net,s代表short(2字节),l代表long(4字节),INADDR_ANY指定地址让操作系统自己获取
双方收发消息
1.服务器等待连接
2.客户端连接服务器
3.双方互发消息
注:可以多个客户端连接,但会出错,因为进程资源竞争
客户端代码
#include
#include
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
int main(int argc,char **argv)
{
if(argc != 3)
{
printf("parameter error\n");
exit(-1);
}
int s_fd;
int r_read;
char msg[128];
char read_buf[128];
struct sockaddr_in s_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
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));*/
//客户端不需要bind
if(connect(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)) == -1)
{
perror("connect");
exit(-1);
}
while(1)
{
if(fork() == 0)
{
while(1)
{
memset(&read_buf,0,sizeof(read_buf));
r_read = read(s_fd,read_buf,128);
printf("read:%d,buf:%s\n",r_read,read_buf);
}
}
while(1)
{
memset(&msg,0,sizeof(msg));
printf("input: ");
gets(msg);
write(s_fd,msg,strlen(msg));
}
}
close(s_fd);
return 0;
}
服务器代码
#include
#include
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
int main(int argc,char **argv)
{
if(argc != 3)
{
printf("parameter error\n");
exit(-1);
}
int s_fd;
int x_fd;
int r_read;
char msg[128];
char read_buf[128];
struct sockaddr_in s_addr;
struct sockaddr_in x_addr;
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&x_addr,0,sizeof(struct sockaddr_in));
s_fd = socket(AF_INET,SOCK_STREAM,0);
if(s_fd == -1)
{
perror("socket");
exit(-1);
}
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));
listen(s_fd,10);
int changdu = sizeof(struct sockaddr_in);
while(1)
{
x_fd = accept(s_fd,(struct sockaddr *)&x_addr,&changdu);//这里必须要变量
printf("client IP:%s\n",inet_ntoa(x_addr.sin_addr));
if(fork() == 0)
{
if(fork() == 0)
{
while(1)
{
memset(&msg,0,sizeof(msg));
printf("input: ");
gets(msg);
write(x_fd,msg,strlen(msg));
}
}
while(1)
{
memset(&read_buf,0,sizeof(read_buf));
r_read = read(x_fd,read_buf,128);
printf("read:%d,buf:%s\n",r_read,read_buf);
}
}
}
close(x_fd);
return 0;
}