目录
1. TCP客户端
- 核心函数
- 完整的TCP客户端程序
2. TCP服务端
- 核心函数
- 完整的TCP客户端程序
TCP编程的核心步骤和流程:
1. TCP客户端
核心函数:
- 创建一个套接字:
int socket(int family,int type,int protocol);
- family: 套接字域
- type: 套接字类型
protocol 套接字协议
- 向服务器发送连接请求:
int connect( int sockfd,
const struct sockaddr *addr,
socklen_t len );
- sockfd:套接字描述符
- addr: 连接的服务器地址结构
len: 地址结构体长度
- 向服务器发送数据:
ssize_t send(int sockfd,
const void* buf,
size_t nbytes,
int flags);
- sockfd: 已建立连接的套接字描述符
- buf: 发送数据的地址
- nbytes: 发送缓数据的大小(以字节为单位)
- flags: 套接字标志(常为 0)
完整的UDP客户端程序
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
unsigned short port; // 服务器的端口号
char *server_ip ; // 服务器ip地址
if( argc > 1 ) //函数传参
{
server_ip = argv[1];
}
if( argc > 2 ) //函数传参
{
port = atoi(argv[2]);
}
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);// 创建通信端点:套接字
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
// 设置服务器地址结构体
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr)); // 初始化服务器地址
server_addr.sin_family = AF_INET; // IPv4
server_addr.sin_port = htons(port); // 端口
//inet_pton(AF_INET, server_ip, &server_addr.sin_addr); // ip
server_addr.sin_addr.s_addr = inet_addr(server_ip);//与inet_pton等价
// 主动连接服务器
int err_log = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if(err_log != 0)
{
perror("connect");
close(sockfd);
exit(-1);
}
printf("send data to %s:%d\n",server_ip,port);
char send_buf[512] = "this is send data";
printf("send data \"%s \" to %s:%d\n",send_buf,server_ip,port);
send(sockfd, send_buf, strlen(send_buf), 0); // 向服务器发送信息
char recv_buf[512] = {0};
recv(sockfd, recv_buf, sizeof(send_buf), 0); // 接收数据
printf("%s\n", recv_buf);
close(sockfd);
return 0;
}
2. TCP服务端
核心函数:
- 本地协议地址与 sockfd 绑定,这样 ip、port 就固定了
int bind( int sockfd,
const struct sockaddr *myaddr,
socklen_t addrlen );
- sockfd:socket 套接字
- myaddr: 指向特定协议的地址结构指针
addrlen:该地址结构的长度
- 将套接字由主动修改为被动,使操作系统为该套接字设置一个连接队列
int listen(int sockfd, int backlog);
- sockfd: socket监听套接字
backlog:连接队列的长度
- 如果没有连接可用,则进入阻塞状态,如果有连接可用则取出一个已经建立的连接
int accept( int sockfd,
struct sockaddr *cliaddr,
socklen_t *addrlen );
- sockfd: socket监听套接字
- cliaddr: 用于存放客户端套接字地址结构
- addrlen: 套接字地址结构体长度的地址
完整的UDP服务端程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
unsigned short port = 8000; // 本地端口
if(argc > 1)
{
port = atoi(argv[1]);
}
//1.创建通信端点:套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket");
exit(-1);
}
//设置本地地址结构体
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr)); // 清空,保证最后8字节为0
my_addr.sin_family = AF_INET; // ipv4
my_addr.sin_port = htons(port); // 端口
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);// ip,INADDR_ANY为通配地址其值为0
//2.绑定:将本地ip、端口与套接字socket相关联起来
int err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));
if( err_log != 0)
{
perror("binding");
close(sockfd);
exit(-1);
}
//3.监听,监听套接字改为被动,创建连接队列
err_log = listen(sockfd, 10);
if(err_log != 0)
{
perror("listen");
close(sockfd);
exit(-1);
}
printf("listen client @port=%d...\n",port);
while(1)
{
struct sockaddr_in client_addr;
char cli_ip[INET_ADDRSTRLEN] = "";
socklen_t cliaddr_len = sizeof(client_addr);
int connfd = 0;
//4.从完成连接队列中提取客户端连接
connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);
if(connfd < 0)
{
perror("accept");
continue;
}
inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
printf("----------------------------------------------\n");
printf("client ip=%s,port=%d\n", cli_ip,ntohs(client_addr.sin_port));
char recv_buf[512] = "";
while( recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0 ) // 接收数据
{
printf("\nrecv data:\n");
printf("%s\n",recv_buf);
}
close(connfd); //关闭已连接套接字
printf("client closed!\n");
}
close(sockfd); //关闭监听套接字
return 0;
}