(本节笔记的实验代码,在这里)
1. TCP编程模型函数化
1.1 server端函数
创建socket——>socket();绑定地址 ——>bind();
监听端口 ——>listen();
等待连接 ——>accept();
接收数据 ——>recv();
发送数据 ——>send();
结束连接 ——>close();
1.2 client端函数
创建socket——> socket();
连接服务器 ——>connect();
接收数据 ——>recv();
发送数据 ——>send();
结束连接 ——>close();
2. 函数学习
2.1创建套接字
函数名:
socket
函数原型: man socket
int socket(int domain, int type, int protocol);
函数功能:
创建网络套接字。
所属头文件:
<sys/types.h> <sys/socket.h>
返回值:
成功 :返回int类型的sockfd 失败 :返回-1
参数说明
domain :AF_INET(IPV4因特网域)
AF_INET6(IPV6因特网域)。
type :SOCK_DGRAM(固定长度的无连接的不可靠的报文传输)
SOCK_RAW(IP协议的数据报接口,在POSIX.1中为可选)
SOCK_SEQPACKET(固定长度的有序的可靠的面向连接的报文传输)
SOCK_STREAM(有序的可靠的双向的面向连接的字节流)。
protocol :通常是0。
2.2 绑定地址
函数名:
bind
函数原型: man 2 bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数功能:
将套接字与地址关联。
所属头文件:
<sys/types.h> <sys/socket.h>
返回值:
成功 :返回0 失败 :返回-1
参数说明
sockfd :需要绑定的套接字ID。
*addr :一个struct sockaddr结构的指针,里面存有server端地址。
//一般用法为(struct sockaddr *)(&struct sockaddr_in)
addrlen :地址长度。
/***********************************************************
struct sockaddr{ //该结构是通用的地质类型,包括IPv4和IP6
sa_family_t sa_family; //协议族
char sa_data[14]; //地址
}
struct sockaddr_in{ //该结构是IPv4专用地址结构
short int sin_family; //协议族,AF_INET
unsigned short int sin_port; //端口号,2字节
struct in_addr sin_addr; //IPv4地址,4字节
unsigned char sin_zero[8]; //用0填充8字节
}
struct in_addr{
unsigned long s_addr; //一个长整形的地址数据,
} //需要把字符串转发为整形
***********************************************************
in_addr_t inet_addr(const char *cp);
<sys/socket.h> <netinet.in.h> <arpa/inet.h>
功能:把字符串形式的IP地址转化为整形的IP地址(网络字节序)。
范例:in_addr.s_addr = inet_addr ("192.168.0.1")
char *inet_ntoa(struct in_addr);
功能:把整形的IP地址转化为字符型的IP地址。
***********************************************************
网络字节序:
网络上通讯过程中,需把发送到网络上的数据转化为网络字节序(大端模式数据),接收数据时,要把数据转化为主机字节序。
<arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
功能:将32位数据从主机字节序转化为网络字节序
范例:in_addr.s_addr = htonl(INADDR_ANY);
uint16_t htons(uint16_t hostshort);
功能:将16位数据从主机字节序转化为网络字节序
uint32_t ntohl(uint32_t netlong);
功能:将32位数据从网络字节序转化为主机字节序
uint16_t ntohs(uint16_t netshort);
功能:将16位数据从网络字节序转化为主机字节序
***********************************************************/
2.3 设置监听端口个数
函数名:
listen
函数原型: man listen
int listen(int sockfd, int backlog);
函数功能:
设置监听端口个数,表示服务器愿意接受连接请求。
所属头文件:
<sys/types.h> <sys/socket.h>
返回值:
成功 :返回0 失败 :返回-1
参数说明
sockfd :server端套接字ID。
backlog :server端愿意接受连接的个数。
函数名:
accept
函数原型: man 2 accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
函数功能:
等待连接请求,接受并建立连接。若无连接请求则进入阻塞。
所属头文件:
<sys/types.h> <sys/socket.h>
返回值:
成功 :返回新的sockfd 失败 :返回-1
参数说明
sockfd :server端的套接字ID。
*addr :一个struct sockaddr结构的指针,里面存有client端地址。
//一般用法为(struct sockaddr *)(&struct sockaddr_in)
*addrlen :client端地址长度(变量)的地址。
2.5 接收数据
函数名:
recv
函数原型: man recv
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
函数功能:
控制如何接收数据。
所属头文件:
<sys/types.h> <sys/socket.h>
返回值:
成功 :返回接收数据的大小 失败 :返回-1
参数说明
sockfd :accept函数返回的新的sockfd。
*buf :接收的数据存放的地址。
len :接收的数据大小。
flags :标志位,如果不需要设置可以直接设为0。
2.6发送数据
函数名:
send
函数原型: man send
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
函数功能:
控制如何发送数据。
所属头文件:
<sys/types.h> <sys/socket.h>
返回值:
成功 :返回发送数据的大小 失败 :返回-1
参数说明:
sockfd :accept函数返回的新的sockfd。。
*buf :需要发送的数据存放地址。
len :发送数据的大小。
flags :标志位,如果不需要设置可以直接设为0。
2.7 关闭连接
函数名:
close
函数原型: man close
int close(int fd);
函数功能:关闭连接。
所属头文件:
<unistd.h>
返回值:
成功 :返回0 失败 :返回-1
参数说明
fd :sockfd。
2.8 连接服务器
函数名:
connect
函数原型: man
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数功能:
client端向server端请求建立连接。
所属头文件:
<sys/types.h> <sys/socket.h>
返回值:
成功 :返回0 失败 :返回-1
参数说明
sockfd :client端sockid。
*addr :一个struct sockaddr结构的指针,里面存有server端地址。
//一般用法为(struct sockaddr *)(&struct sockaddr_in)
addrlen :client端地址长度。
3. 综合实例
/* touch server.c */
/* 利用TCP协议进行网络通讯的服务器端程序 */
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#define PORTNUM 3333
int main()
{
int sockfd;
int app_sockfd;
int recv_sz;
long addr_len;
char buff[128];
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
/* 1.创建socket */
if(-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
printf("create socket error!\n");
exit(1);
}
/* 2.绑定地址 */
bzero(&server_addr, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORTNUM);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr));
/* 3.监听端口 */
listen(sockfd, 5);
while(1)
{
addr_len = sizeof(struct sockaddr);
/* 4.等待连接 */
app_sockfd = accept(sockfd, (struct sockaddr *)(&client_addr), (socklen_t *)(&addr_len));
printf("server get connection from:%s\n", inet_ntoa(client_addr.sin_addr));
/* 5.接收数据 */
recv_sz = recv(app_sockfd, buff, 128, 0);
buffer[recv_sz] = '\0';
printf("server received %s\n", buff);
/* 6.结束连接 */
close(app_sockfd);
}
/* 7.结束连接 */
close(sockfd);
return 0;
}
/* touch client.c */
/* 利用TCP协议进行网络通讯的客户端程序 */
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#define PORTNUM 3333
int main()
{
int sockfd;
int send_sz;
char buff[128];
struct sockaddr_in server_addr;
/* 1.创建socket */
if(-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
{
printf("create socket error!\n");
exit(1);
}
/* 2.连接服务器 */
bzero(&server_addr, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORTNUM);
server_addr.sin_addr.s_addr = inet_addr("192.168.123.4");
if(-1 == connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr))
{
printf("connet fail!\n");
exit(1);
}
/* 4.发送数据 */
printf("Please input something:\n");
fgets(buff, 128, stdin);
send(sockfd, buffe, strlen(buffer), 0);
/* 5.结束连接 */
close(sockfd);
return 0;
}