协议:一组规则
分层模型结构:
C/S模型和B/S模型
网络传输流程:
以太网帧协议:
IP协议:
192.168.1.108 --- 点分十进制 IP地址(string)
IP地址:可以在网络环境中,唯一标识一台主机
端口号:可以进行网络通信的一台主机上,唯一标识一个进程
IP地址+端口号:可以在网络环境中,唯一标识一个进程
UDP协议
16位:源端口号 2^16 = 65536
16位:目的端口号
TCP协议
16位:源端口号 2^16 = 65536
16位:目的端口号
32序号
32确认序号
6个标志位
16位窗口大小 2^16 = 65536
网络套接字:socket
网络字节序:
htonl --> 本地--》网络 (IP) 192.168.1.11 --> string --> atoi --> int --> htonl --> 网络字节序
htons --> 本地--》网络 (port)
ntohl --> 网络--》 本地(IP)
ntohs --> 网络--》 本地(Port)
注意:htonl --> 本地 --> 网络(IP)
192.168.1.11 --> string --> atoi --> int --> htonl --> 网络字节序
IP地址转换函数
// 本地字节序(string IP) ---> 网络字节序
int inet_pton(int af, const char *src, void *dst);
af: AF_INET,AF_INET6
src:传入,IP地址(点分十进制)
dst:传出转换后的 网络字节序的 IP地址
返回值:
成功: 1
异常: 0,说明src指向的不是一个有效的ip地址
失败: -1
NAME
inet_pton - convert IPv4 and IPv6 addresses from text to binary form
SYNOPSIS
#include
int inet_pton(int af, const char *src, void *dst);
DESCRIPTION
This function converts the character string src into a network address
structure in the af address family, then copies the network address
structure to dst. The af argument must be either AF_INET or AF_INET6.
dst is written in network byte order.
// 网络字节序 ---> 本地字节序(string IP)
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
af: AF_INET,AF_INET6
src: 网络字节序IP地址
dst: 本地字节序(string IP)
size: dst的大小
返回值:
成功: dst
失败: NULL
NAME
inet_ntop - convert IPv4 and IPv6 addresses from binary to text form
SYNOPSIS
#include
const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);
DESCRIPTION
This function converts the network address structure src in the af
address family into a character string. The resulting string is copied
to the buffer pointed to by dst, which must be a non-null pointer. The
caller specifies the number of bytes available in this buffer in the
argument size.
sockaddr地址结构: IP + Port --> 在网络环境中唯一标识一个进程
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
#if 0
int dst;
inet_pton(AF_INET,"192.168.1.100",(void*)dst);
addr.sin_addr.s_addr = dst;
#else
// 取出系统中有效的任意IP地址(二进制类型)
addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(fd,(struct sockaddr*)&addr,sizeof(addr));
socket函数
#include
// 创建一个套接字
int socket(int domain, int type, int protocol);
domain: AF_INET,AF_INET6
type: SOCK_STREAM,SOCK_DGRAM
protocol: 0
返回值:
成功: 新套接字所对应文件描述符
失败: -1 errno
NAME
socket - create an endpoint for communication
SYNOPSIS
#include /* See NOTES */
#include
int socket(int domain, int type, int protocol);
DESCRIPTION
socket() creates an endpoint for communication and returns a file
descriptor that refers to that endpoint. The file descriptor returned
by a successful call will be the lowest-numbered file descriptor not
currently open for the process.
#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd: socket 函数返回值
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr:传入参数(struct sockaddr*)&addr
addrlen:sizeof(addr) 地址结构的大小
返回值:
成功: 0
失败: -1 errno
NAME
bind - bind a name to a socket
SYNOPSIS
#include /* See NOTES */
#include
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
DESCRIPTION
When a socket is created with socket(2), it exists in a name space
(address family) but has no address assigned to it. bind() assigns the
address specified by addr to the socket referred to by the file descrip‐
tor sockfd. addrlen specifies the size, in bytes, of the address struc‐
ture pointed to by addr. Traditionally, this operation is called
“assigning a name to a socket”.
It is normally necessary to assign a local address using bind() before a
SOCK_STREAM socket may receive connections (see accept(2)).
// 设置同时与服务器建立连接的上限数(同时进行3次握手的客户端数量)
int listen(int sockfd, int backlog);
sockfd: socket 函数返回值
backlog: 上限数值,最大值为128
返回值:
成功: 0
失败: -1 errno
NAME
listen - listen for connections on a socket
SYNOPSIS
#include /* See NOTES */
#include
int listen(int sockfd, int backlog);
DESCRIPTION
listen() marks the socket referred to by sockfd as a passive socket,
that is, as a socket that will be used to accept incoming connection
requests using accept(2).
The sockfd argument is a file descriptor that refers to a socket of type
SOCK_STREAM or SOCK_SEQPACKET.
The backlog argument defines the maximum length to which the queue of
pending connections for sockfd may grow. If a connection request
arrives when the queue is full, the client may receive an error with an
indication of ECONNREFUSED or, if the underlying protocol supports
retransmission, the request may be ignored so that a later reattempt at
connection succeeds.
// 阻塞等待客户端建立连接,成功的话,返回一个与客户端成功连接的socket文件描述符
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:socket 函数返回值
addr:传出参数,成功与服务器建立连接的那个客户端的地址结构(IP+port)
socklen_t client_addr_len = sizeof(addr);
addrlen:传入传出 &client_addr_len
入:addr的大小
出:客户端addr实际大小
返回值:
成功:能与客户端进行数据通信的 socket 对应的文件描述
失败:-1,errno
NAME
accept, accept4 - accept a connection on a socket
SYNOPSIS
#include /* See NOTES */
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include
int accept4(int sockfd, struct sockaddr *addr,
socklen_t *addrlen, int flags);
DESCRIPTION
The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET). It extracts
the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new
connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in
the listening state. The original socket sockfd is unaffected by this call.
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd: socket 函数返回值
struct sockaddr_in server_addr; // 服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);// 跟服务器bind时设定的 port 完全一致。
inet_pton(AF_INET, "服务器IP地址", &server_addr.sin_addr.s_addr);
addr:传入参数,服务器的地址结构
addrlen:服务器的地址结构的大小
返回值:
成功: 0
失败: -1,errno
如果不使用bind绑定客户端地址结构,采用"隐式绑定".
NAME
connect - initiate a connection on a socket
SYNOPSIS
#include /* See NOTES */
#include
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
DESCRIPTION
The connect() system call connects the socket referred to by the file
descriptor sockfd to the address specified by addr. The addrlen argu‐
ment specifies the size of addr. The format of the address in addr is
determined by the address space of the socket sockfd; see socket(2) for
further details.
If the socket sockfd is of type SOCK_DGRAM, then addr is the address to
which datagrams are sent by default, and the only address from which
datagrams are received. If the socket is of type SOCK_STREAM or
SOCK_SEQPACKET, this call attempts to make a connection to the socket
that is bound to the address specified by addr.
Generally, connection-based protocol sockets may successfully connect()
only once; connectionless protocol sockets may use connect() multiple
times to change their association. Connectionless sockets may dissolve
the association by connecting to an address with the sa_family member of
sockaddr set to AF_UNSPEC (supported on Linux since kernel 2.2).
TCP 通信流程分析:
TCP 通信流程分析:
Server:
1.socket() 创建socket
2.bind() 绑定服务器地址结构
3.listen() 设置同时与服务器建立连接的上限数(监听上限)
4.accept() 阻塞监听客户端连接
5.read(fd) 读socket获取客户端数据
6.小 -- 大写 toupper()
7.write(fd)
8.close()
Client:
1.socket() 创建socket
2.connect() 与服务器建立连接
3.write() 写数据到socket
4.read() 读转换后的数据
5.显示读取结果
6.close()
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 9527
void sysErr(const char *msg) {
perror(msg);
exit(1);
}
int main(int argc, char *argv[]) {
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if (lfd == -1) sysErr("socket error");
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//给服务器socket绑定地址结构
int ret = bind(lfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret == -1) sysErr("bind error");
// 设置监听上限
ret = listen(lfd,128);
if(ret == -1) sysErr("listen error");
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);// 获取客户端地址结构大小
// 阻塞等待客户端连接请求
int cfd = accept(lfd,(struct sockaddr*)&client_addr,&client_addr_len);
if(cfd == -1) sysErr("accept error");
// 根据accept传出参数,获取客户端 ip 和 port
char client_IP[1024];
printf("client ip:%s port:%d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),
ntohs(client_addr.sin_port));
char buf[BUFSIZ];
while(1) {
ret = read(cfd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,ret); // 写到屏幕查看
// 转换为大写
for(int i=0;i
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 9527
void sysErr(const char* msg) {
perror(msg);
exit(EXIT_FAILURE);// EXIT_FAILURE 1
}
int main(int argc, char const *argv[]) {
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd == -1) sysErr("socket error");
struct sockaddr_in server_addr; // 服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr.s_addr);
int ret = connect(cfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(ret == -1) sysErr("connect error");
int counter = 10;
char buf[BUFSIZ];
while(counter--) {
char *msg = "hello server\n";
write(cfd,msg,strlen(msg));
ret = read(cfd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,ret);
sleep(1);
}
close(cfd);
return 0;
}
heheda@linux:~/Linux/test$ gcc server.c -o server -Wall -g
heheda@linux:~/Linux/test$ ./server
heheda@linux:~/Linux/test$ gcc client.c -o client -Wall -g
heheda@linux:~/Linux/test$ ./client