1. Socket()
在利用套接字进行网络通信时,进程要做的第一件事就是调用socket(),产生一个套接字,并指明将要使用的通信协议,如TCP、UDP、XNS,SPP等。
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 int socket(int family, int type, int protocol);
// 函数成功返回一个非负的整数值,否则返回-1.
参数解析:
family: 指明协议族
- AF_INET,
- AF_INET6
- AF_UNIX,
- AF_ISO,
- AF_ROUTE
type: 表示套接字类型.
- SOCK_STREAM
- SOCK_DGRAM
- SOCK_RAW
protocol: 表示指定所用协议。 对于大多数应用protocol都被设置为0, 表示使用默认协议。
2. bind()
调用socket函数创建套接字后,存在一个名字空间,但它没有命名。bind()将套接字地址(本地主机地址和本地端口地址)与所创建的套接字句柄联系起来,即将名字赋予套接字,以指定本地址中的{协议,本地地址,本地端口}
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 int bind(int fd, struct sockaddr *addressp, int addrlen);
fd: 由socket()函数返回的套接字描述符。
addressp: 向协议传送地址的指针,包含有关的地址信息: 名称、端口和IP地址。
addrlen: 地址结构的字节数.
bind()把传送地址与套接字连
接在一起,调用成功返回0,否则返回-1.
3. connect()
客户进程在用socket()产生套接字后,用connect()将该套接字与服务器套接字相连接。
#include <stdio.h>
#include <sys/socket.h>
int connect(int fd, struct sockaddr *addressp, int addren);
// 成功返回0, 出错返回-
fd: 套接字描述符
addressp: 套接字地址指针
addrlen: 地址结构的字节数。
4. listen()函数
当服务器完成当前请求以前又发生多个服务请求时,服务器可以拒绝到来的服务请求。为了更好地处理这些问题,服务器可以使用listen()函数将所有的服务请求放在一个请求队列中排除,并尽快地处理这些请求。
socket执行体建立一个输入请求队列,将到达的服务请求保存在此队列上,直到处理完它们为止,即listen()函数不仅使socket处于被动侦听状态,同时告诉socket执行体对此socket处理多个同时的服务请求。
1 #include <sys/socket.h>
2 int listen(int fd, int qlen);
fd: 套接字文件描述符,只能是SOCK_STREAM, SOCK_SEQPACKET类型
qlen:连接请求队列长度。
成功返回0,否则-1
当一个连接请求到达时,被插入请求队列。服务器用accept()函数从队列中移走一个请求并响应这个请求。
5. accept()
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 int accept(int fd, sockaddr * addressp, int *addrlen);
如果调用accept()是队列上无连接请求,那么此调用在一个请求到达前阻塞调用者的运行,accept()返回-1,errno置为ewouldblock, 指出无连接请求到达。
6. read() 与 write()
这两个函数用来接收和发送数据的两个函数。 read()用于从套接字缓冲区读取数据, write() 用于往套接字缓冲区写数据
int read(int fd, char *buf, int len);
int write(int fd, char *buf, int len);
7. close()
用于关闭套接字,并立即返回到进程。
TCP 三次握手,TCP 四次关闭
第一步:客户端发起SYN请求,状态变成TCP_SYN_SENT
第二步:服务器端状态TCP_LISTEN, 当收到客户端的的SYN包的时候,变成TCP_SYN_RECV,
发送SYN ACK
第三步:客户端收到SYN_ACK, 将状态变成TCP_ESTABLISHED,并且发送ACK
第四步:服务器收到SYN ACK, 将状态变成TCP_ESTABLISH
TCP 状态转换图 net/ipv4/tcp_input.c 中 tcp_rcv_state_process() 方法
include/net/tcp_states.h
enum {
TCP_ESTABLISHED = 1,
TCP_SYN_SENT,
TCP_SYN_RECV,
TCP_FIN_WAIT1,
TCP_FIN_WAIT2,
TCP_TIME_WAIT,
TCP_CLOSE,
TCP_CLOSE_WAIT,
TCP_LAST_ACK,
TCP_LISTEN,
TCP_CLOSING, /* Now a valid state */
TCP_NEW_SYN_RECV,
TCP_MAX_STATES /* Leave at the end! */
};
例题: 服务器通过socket连接向客户端发送字符串“Hello, you are connected!”。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <netinet/in.h>
7 #include <sys/socket.h>
8 #include <sys/wait.h>
9
10 #define SERVPORT 3333 //服务器监听端口号
11 # define BACKLOG 10 //最大同时连接请求数
12
13 main()
14 {
15 int sockfd, client_fd; //sock_fd: 监听socket; client_fd: 数据传输socket
16 int sin_size;
17 struct sockaddr_in my_addr; //本机地址信息
18 struct sockaddr_in remote_addr; //客户端地址信息
19
20 if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
21 perror("socket creat error!");
22 exit(-1);
23 } //建立套接字
24
25 my_addr.sin_family = AF_INET;
26 my_addr.sin_port=htons(SERVPORT);
27 my_addr.sin_addr.s_addr = INADDR_ANY;
28 bzero(&(my_addr.sin_zero), 8);
29
30 if(bind(sockrfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) ==-1)
31 {
32 perror("bind error!");
33 exit(1);
34 } //绑定
35
36 if(listen(sockfd, BACKLOG) == -1)
37 {
38 perror("listen error!");
39 exit(1);
40 }//监听
41
42 while(1){
43 sin_size=sizeof(struct sockaddr_in);
44 if((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1)
45 {
46 perror("accept error!");
47 continue;
48 }
49 printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
50 if(!fork()){
51 if(send(client_fd, "Hello, you are connected!\n", 26, 0) ==-1)
52 perror("send error!");
53 close(client_fd);
54 exit(0);
55 }
56 close(client_fd);
57 }
58 }