文章目录
- 1. code
- 2. TCP连接过程
- 3. 服务端用到的几个网络编程api
- 3.1. 建立socket套接字
- 3.2. sockaddr_in结构
- 3.3. bzero置零函数
- 3.4. 字节序转换hton函数
- 3.5. bind将ip和端口绑定到socket
- 3.6. 监听套接字listen
- 3.7. 接受连接accept
- 4. 客户端用到的api
- 4.1. 发起连接请求connect
- 5. 参考
以回显服务器为例,整理一下网络编程的基础知识。
code
服务端代码
代码语言:javascript复制#include <iostream>
#include "unp.h"
using namespace std;
int main(){
int listenfd,connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr,servaddr;
listenfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(SERV_PORT);
bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
listen(listenfd,LISTENQ);
int i=0;
for(;;){
clilen=sizeof(cliaddr);
connfd=accept(listenfd,(SA*)&cliaddr,&clilen);
if((childpid=fork())==0){
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
i ;
cout<<i<<" "<<endl;
}
return 0;
}
void str_echo(int sockfd){
ssize_t n;
char buf[MAXLINE];
again:
while((n=read(sockfd,buf,MAXLINE))>0){
write(sockfd,buf,n);
}
if(n<0 && errno==EINTR){
goto again;
}else if(n<0){
cout<<"str_echo: read error"<<endl;
}
}
客户端程序
代码语言:javascript复制#include <iostream>
#include "unp.h"
using namespace std;
int main(int argc,char **argv){
int sockfd;
struct sockaddr_in servaddr;
if(argc!=2){
cout<<"usage:echoclie <ip addr>"<<endl;
return 0;
}
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0){
cout<<"socket create error"<<endl;
return 0;
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(SERV_PORT);
inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
int conn=connect(sockfd,(SA*)&servaddr,sizeof(servaddr));
if(conn<0){
cout<<"connect create error"<<endl;
return 0;
}
str_cli(stdin,sockfd);
return 0;
}
void str_cli(FILE *fp,int sockfd){
char sendline[MAXLINE],recvline[MAXLINE];
while(fgets(sendline,MAXLINE,fp) != NULL){
write(sockfd,sendline,strlen(sendline));
//if(readline(sockfd,recvline,MAXLINE)==0){
if(read(sockfd,recvline,MAXLINE)<0){
cout<<"str_cli:server terminated prematurely"<<endl;
}
fputs(recvline,stdout);
}
}
TCP连接过程
服务端用到的几个网络编程api
建立socket套接字
函数原型
代码语言:javascript复制int socket(int domain, int type, int protocol);
domain 具体通信的域
AF_UNIX Local communication
AF_LOCAL Synonym for AF_UNIX
AF_INET(常用) IPv4 Internet protocols
type 通信类型
SOCK_STREAM(常用) Provides sequenced, reliable, two-way, connection-
based byte streams. An out-of-band data transmission
mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable
messages of a fixed maximum length).
protocol 用来设置用tcp还是udp,一般为0
sockaddr_in结构
代码语言:javascript复制include <netinet/in.h>
struct sockaddr {
unsigned short sa_family; // 2 bytes address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
// IPv4 AF_INET sockets:
struct sockaddr_in {
short sin_family; // 2 bytes e.g. AF_INET, AF_INET6
unsigned short sin_port; // 2 bytes e.g. htons(3490)
struct in_addr sin_addr; // 4 bytes see struct in_addr, below
char sin_zero[8]; // 8 bytes zero this if you want to
};
struct in_addr {
unsigned long s_addr; // 4 bytes load with inet_pton()
};
sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别: 程序员不应操作sockaddr,sockaddr是给操作系统用的 程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。 一般的用法为: 程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数
bzero置零函数
memset()函数也可完成这个功能,但是在填写参数时容易出错并且编译器不会提示,所以改用bzero
代码语言:javascript复制include <strings.h>
void bzero(void *s, size_t n);
%%%%
bzero(&servaddr,sizeof(servaddr));
字节序转换hton函数
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
代码语言:javascript复制#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
用来将主机字节序的无符号整型转换为网络字节序
bind将ip和端口绑定到socket
代码语言:javascript复制bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
函数原型:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd socket套接字的描述符
addr 储存ip和端口
addrlen 前一个参数结构体的所占字节长度
监听套接字listen
listen(listenfd,LISTENQ);
代码语言:javascript复制include <sys/socket.h>
int listen(int socket,int backlog)
socket 监听的套接字描述符
backlog
系统维护两个队列,分别是已完成三路握手的队列和未完成的,两个队列的长度不会超过backlog
接受连接accept
代码语言:javascript复制int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
该函数会提取未完成队列中的第一个连接请求,完成三路握手,然后返回一个文件描述符,后续可以用来读写。
如果未完成队列为空,则默认会阻塞。
客户端用到的api
发起连接请求connect
代码语言:javascript复制int conn=connect(sockfd,(SA*)&servaddr,sizeof(servaddr));
客户端有connect()来发起请求。
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参考
- 《UNIX网络编程(卷一)》
- sockaddr和sockaddr_in的区别
欢迎与我分享你的看法。 转载请注明出处:http://taowusheng.cn/