TCP/IP协议族体系是Internet事实上的工业标准。
一共有四层
应用层 | Relnet,FTP,HTTP,DNS,SMTP等 |
---|---|
传输层 | TCP和UDP |
网络层 | IP,ICMP和IGMP,端到端传输 |
网络接口和物理层 | 以太网,令牌环网,FDDI,wifi,gps/2G/3G/4G,驱动(屏蔽硬件差异) |
是一个编程接口,是一个特殊的文件描述符(对他执行IO的操作函数,比如read,write,close等),并不仅限于TCP/IP协议,面向连接TCP,无连接UDP。
socket代表网络编程的一种资源
分类:
定义:字节序是指多字节数据在计算机内存中存储或网络传输时各字节的存储顺序
常见的字节序:
字节序是指不同的CPU访问内存中的多字节数据时候,存在大小端的问题
#include
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(两个字节),l代表long(4个字节),
通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。
有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取
函数原型:
int socket(int domain, int type, int protocol);
参数:
注意:
函数原型:
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
参数:
注意:
第二个参数需要强制类型装换为(struct sockaddr*),因为addr指针变量是由结构体struct sockaddr_in 创建的
使用sockaddr_in结构体替换sockaddr结构体:
协议族选:AF_INET
第三个参数为结构体,配置结构体变量in_addr的成员s_addr,调用inet_aton()方法将其转化为网络字节序
struct in_addr{
uint32_t s_addr;
}
实例:
#include
#include
int inet_aton(const char* straddr,struct in_addr *addrp);
//把字符串形式的“192.168.1.123”转为网络能识别的格式
例如:在bind函数的结构体中使用
struct sokeaddr_in s_addr;
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8989);//端口号需要转化
inet_aton("127.0.0.1",&s_addr.sin_addr)
//第二个参数结构体指针,指向in_addr 结构体,所以取地址取到该结构体即可
char* inet_ntoa(struct in_addr inaddr);
//把网络格式的ip地址转为字符串形式
linux中查询该结构体定义的位置:
cd /user/include/
grep “struck sockaddr_in {” * -nir
大括号可以搜索到声明的地方
*是当前目录下
r是递归寻找
n是显示行号
i是不区分大小写
头文件是
vi linux/in.h + 184
函数原型:
int listen(int sockfd,int backlog);
参数:
注意:
函数原型:
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
返回值:
参数与功能:
printf("%s\n",inet_ntoa(c_addr.sin_addr));
#include
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));
在window的控制台输入
ping 服务器地址或telnet 服务器地址 端口号
函数原型:
ssize_t write(int fd,const void *buf,size_t nbytes);
ssize_t read(int fd,void *buf,size_t nbyte);
//函数返回值为读写的字节个数,错误则返回-1
//返回0表示客户端退出
注意:
此时的fd为accept的返回值,而不是socket()返回值
在套接字通信中进行字节读取函数与IO读取的略语有区别,因为他们输入或输出的字节数比请求的少
网络I/O操作:(一)read()/write()(二)recv()/send()(三)readv()/writev()(四)recvmsg()/sendmsg()(五)recvfrom()/sendto()
ssize_t send(int s,const void *msg,size_t len,int flags);
参数:
ssize_t recv(int s,void *buf,size_t len,int flags);
函数原型:
int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
参数:
注意:
ssize_t send(int sockfd,const void *buf,size_t len,int flags);
参数:
sockfd:socket函数返回的fd
buffer:发送缓冲区首地址
length:发送的字节
flags:发送方式(通常为0),作用和write一样
MSG_DONTWAIT,非阻塞
MSG_OOB:用于TCP类型的带外数据(out of band)
返回值:
成功:实际发送的字节数
失败:-1,并设置errno
int recv( SOCKET s, char FAR *buf, int len, int flags);
flag:一般填0,和read作用一样
特殊的标志:
MSG_DONTWAIT
MSG_OOB:读取带外数据
MSG_PEEK:流
1.TCP多线程服务器
2.TCP多进程并发服务器
在网络进程中,父进程等待客户端的服务请求,当这种请求到达时,父进程调用fork,使得子进程处理该请求,父进程继续等待下一个服务请求的到达
服务端
#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[128]={0};
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
if(argc !=3){
printf("param is not enough");
exit(-1);
}
memset(&s_addr,0,sizeof(struct sockaddr_in));
memset(&c_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 clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd,(struct sockaddr*)&c_addr,&clen);
if(c_fd == -1 ){
perror("accept");
}
if(fork()==0){
if(fork()==0){
while(1){
memset(msg,0,sizeof(msg));
printf("input:");
fgets(msg,128,stdin);
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 from client:%s\n",readBuf);
}
}
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[128]={0};
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
if(argc !=3){
printf("param is not enough");
exit(-1);
}
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);
if(connect(c_fd,(struct sockaddr*)&c_addr,sizeof(struct sockaddr))==-1){
perror("connect");
exit(-1);
}
while(1){
if(fork()==0){
while(1){
memset(msg,0,sizeof(msg));
printf("input:");
fgets(msg,128,stdin);
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 from server:%d,%s\n",n_read,readBuf);
}
}
break;
}
return 0;
}
注意:
优秀博文:
https://blog.51cto.com/u_15467009/4876970
https://www.cnblogs.com/MyLove-Summer/p/5215287.html