触发学习UNIX网络编程的动力在于前段时间需要开发一个接入服务,需要考虑比较高的并发处理能力,且尽量少占用的机器资源,选用了JAVA的Netty框架,学习过程产生不少疑问,限于基础知识太薄弱无法理解原理,所以开始关注UNIX编程。
学习资料是这本书,书有砖头那么大,知识点非常多,分几个小章节记录汇总。这个章节作为开篇,记录最基础的API使用指引。
有C语言基础都可以看得懂的Server和Client端代
Server端代码
代码语言:javascript复制#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#define MAXLINE 4096
#define LISTENQ 1024
int main(int argc , char **argv ){
int listenfd, connfd, n;
char buff[MAXLINE 1];
struct sockaddr_in servaddr;
// socket
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(13);
// bind
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
// listen
listen(listenfd,LISTENQ);
for(;;){
connfd = accept(listenfd,NULL,NULL);
while( ( n= read(connfd,buff,MAXLINE)) > 0 ){
printf("receive message from client. message = %s",buff);
write(connfd,buff,strlen(buff));
bzero(&buff,sizeof(buff));
}
close(connfd);
}
}
Client端代码
代码语言:javascript复制#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAXLINE 4096
#define LISTENQ 1024
int
main(int argc , char **argv ){
int sockfd, n;
char sendline[MAXLINE 1] ,recvline[MAXLINE 1];
struct sockaddr_in servaddr;
// socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(13);
inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);
// connect
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
while( fgets(sendline,MAXLINE,stdin) != NULL ){
write(sockfd,sendline,strlen(sendline));
bzero(&recvline,sizeof(recvline));
if( (n = read(sockfd,recvline,MAXLINE) ) > 0 ){
recvline[n] = 0 ;
printf("receive message from server. message = %s",recvline);
// fputs(recvline,stdout);
}
bzero(&sendline,sizeof(sendline));
}
close(sockfd);
exit(0);
}
程序运行示例
这是一个简单的回射程序,不管客户端发送什么消息,服务器端接受到消息之后原样返回给客户端。不包含任何错误判断和错误处理。
关键函数注解
代码语言:javascript复制1、socket函数
int socket( int family , int type , int protocol );
创建一个套接字文件,返回套接字文件描述符
[family]
指定协议簇 : AF_INET(IPv4) AF_INET6(IPv6)
AF_LOCAL(UNIX协议) AF_ROUTE(路由套接字) AF_KEY(秘钥套接字)
[type]
指定套接字类型 : SOCK_STREAM(字节流套接字)、
SOCK_DGRAM(数据报套接字)、SOCK_SEQPACKET(有序分组套接字)、
ROCK_RAW(原始套接字)
[protocol]
指定协议类型:IPPROTO_TCP(TCP传输协议)、
IPPROTO_UDP(UDP传输协议)、IPPROTO_SCTP(STCP传输协议),
设置为0可以表示选择给定family和type组合的系统默认值。通常设置为0。
代码语言:javascript复制2、bind函数
int bind(int sockfd , const struct sockaddr *myaddr ,
socklen_t addrlen);
把一个本地协议地址赋予一个套接字
[sockfd]
socket函数返回的套接字描述符
[myaddr]
是指向本地IP地址的通用套接字结构
[addrlen]
特定套接字结构的长度(IPv4、IPv6、Unix域、数据链路、存储等)
代码语言:javascript复制3、listen函数
int listen( int sockfd , int backlog );
把一个本地协议地址赋予一个套接字,把套件字变更为被动连接。
[sockfd]
socket指示内核接受指向该套接字的连接请求。
[backlog]
未完成三次握手的请求 已完成三次握手的请求总和。是一个大约值。
代码语言:javascript复制4、accept函数
int accept( int sockfd , struct sockaddr *cliaddr ,
scoklen_t *addrlen );
从已完成连接队列队头返回下一个已完成连接,如果丢列为空,
进程阻塞进入睡眠。
[sockfd]
socket等待接受连接的侦听套接字。
[cliaddr]
客户端协议地址。如果不关注客户端的地址,可以设置为NULL。
[addrlen]
客户端协议地址的端口号。如果不关注,可以设置为NULL。
代码语言:javascript复制5、connect函数
int connect( int sockfd , const struct sockaddr * servaddr ,
socklen_t addrlen );
TCP客户端发起跟服务器的连接。
[sockfd]
由socket函数创建的套接字连接
[servaddr]
包含服务器IP地址和端口号的套接字地址结构
[addrlen]
套接字地址结构的大小。
代码语言:javascript复制bzero 字节操纵函数,用于把目的结构体中指定数目的字节置为0
类似于memset函数,但是比memset少一个容易出BUG的参数
在C语言里,对一块刚申请的内存先清零再使用是必须的
htonlhtons 字节排序函数,
htonl是对32位的IPv4地址做转换
htons是对16为的端口号做转换
由机器字节序转变为网络字节序,网际协议使用大端字节序来表示
字符,而机器则是不同操作系统使用不同的字节序
read 从连接套接字中读取指定长度的内容
write 往连接套接字中写取指定长度的内容
inet_pton 把字符串格式的IP地址,转成相应协议族的数值格式
另外一个配套的函数是inet_ntop,作用相反
fgets 函数,从标准输入中读取指定长度字符串,有点像scanf
基础知识补齐
1、Socket在OSI(开放系统互联)模型——7层网络模型中的位置
Socket就像一个插头,联通应用层中的应用与网络设备,应用要提供网络服务,或者需要网络服务都得通过Socket的API进行。
2、Socket API的数据流
下周文章提纲
1、C语言错误处理
2、项目文件组织与编写Makefile
3、完成server和client中其他函数的包裹