Linux C语言实现TCP客户端与服务器
作者:互联网
本文中的TCP server 和 client,为基于软件层面(TCP/IP协议栈)的Socket应用开发。
一.Socket
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。
数据结构:注意sockaddr_in 和 sockaddr的区别(前者具体,后者抽象)
#ifdef CONFIG_NET_IPv6
struct sockaddr_storage
{
sa_family_t ss_family; /* Address family */
char ss_data[18]; /* 18-bytes of address data */
};
#else
struct sockaddr_storage
{
sa_family_t ss_family; /* Address family */
char ss_data[14]; /* 14-bytes of address data */
};
#endif
/* The sockaddr structure is used to define a socket address which is used
* in the bind(), connect(), getpeername(), getsockname(), recvfrom(), and
* sendto() functions.
*/
//抽象的socket地址,不区分IPV4,IPV6
//14字节 = 2(端口) + 4(ip地址) + 8(保留)
struct sockaddr
{
sa_family_t sa_family; /* Address family: See AF_* definitions */
char sa_data[14]; /* 14-bytes of address data */
};
//IPV4协议,将 2(端口) + 4(ip地址)分开表示
struct sockaddr_in
{
sa_family_t sin_family; /* Address family: AF_INET */
uint16_t sin_port; /* Port in network byte order */
struct in_addr sin_addr; /* Internet address */
};
//IPV6
struct sockaddr_in6
{
sa_family_t sin6_family; /* Address family: AF_INET6 */
uint16_t sin6_port; /* Port in network byte order */
struct in6_addr sin6_addr; /* IPv6 internet address */
};
函数接口:
int socket(int domain, int type, int protocol);
int bind(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen);
int connect(int sockfd, FAR const struct sockaddr *addr, socklen_t addrlen);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
ssize_t send(int sockfd, FAR const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, FAR const void *buf, size_t len, int flags,
FAR const struct sockaddr *to, socklen_t tolen);
ssize_t recv(int sockfd, FAR void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, FAR void *buf, size_t len, int flags,
FAR struct sockaddr *from, FAR socklen_t *fromlen);
二.TCP 与 UDP
TCP(Transmission Control Protocol,传输控制协议)是面向连接的,基于数据流的可靠协议,在正式通信之前必须建立起连接。UDP(User Data Protocol,用户数据报协议)是一个非连接的数据报协议。TCP的服务器模式比UDP的服务器模式多了listen,accept函数。TCP客户端比UDP客户端多了connect函数。
这里只谈TCP的客户端与服务器。
三.TCP server
TCP server的实现流程:
1、创建一个socket,用函数socket();
2、绑定IP地址、端口等信息到socket上,用函数bind();
3、开启监听,用函数listen();
4、接收客户端上来的连接,用函数accept();
5、收发数据,用函数send()和recv(),或者read()和write();
6、关闭网络连接;
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#define SERVPORT 4444
#define BACKLOG 10
#define MAXDATASIZE 15
int main() {
struct sockaddr_in server_sockaddr;//声明服务器socket存储结构
int sin_size,recvbytes;
int sockfd,client_fd;//socket描述符
char buf[MAXDATASIZE];//传输的数据
//1.建立socket
//AF_INET 表示IPV4
//SOCK_STREAM 表示TCP
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
perror("Socket");
exit(1);
}
printf("Socket successful!,sockfd=%d\n",sockfd);
//以sockaddt_in结构体填充socket信息
server_sockaddr.sin_family = AF_INET;//IPv4
server_sockaddr.sin_port = htons(SERVPORT);//端口
server_sockaddr.sin_addr.s_addr = INADDR_ANY;//本主机的任意IP都可以使用
bzero(&(server_sockaddr.sin_zero),8);//保留的8字节置零
//2.绑定 fd与 端口和地址
if((bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))) < 0) {
perror("bind");
exit(-1);
}
printf("bind successful !\n");
//3.监听
if(listen(sockfd,BACKLOG) < 0) {
perror("listen");
exit(1);
}
printf("listening ... \n");
while(1){
//4.接收请求,函数在有客户端连接时返回一个客户端socket fd,否则则阻塞
//优化:这里同样可以使用select,以及poll来实现异步通信
if((client_fd = accept(sockfd,NULL,&sin_size)) == -1) {
perror("accept");
exit(1);
}
printf("accept success! client_fd:%d\n",client_fd);
//5.接收数据
//注意:这里传入的fd,不是建立的socket fd,而是accept返回的连接客户端 socket fd
if((recvbytes = recv(client_fd,buf,MAXDATASIZE,0)) == -1) {
perror("recv");
exit(1);
}
printf("received data : %s\n",buf);
}
//6.关闭
close(sockfd);
}
四.TCP client
TCP client的实现流程:
1、创建一个socket,用函数socket();
2、连接服务器,用函数connect();
3、收发数据,用函数send()和recv(),或者read()和write();
4、关闭网络连接;
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#define SERVPORT 4444
int main(int argc,char *argv[]) {
int sockfd,sendbytes;
struct sockaddr_in serv_addr;//需要连接的服务器地址信息
//1.创建socket
//AF_INET 表示IPV4
//SOCK_STREAM 表示TCP
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
perror("socket");
exit(1);
}
//填充服务器地址信息
serv_addr.sin_family = AF_INET; //网络层的IP协议: IPV4
serv_addr.sin_port = htons(SERVPORT); //传输层的端口号
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.xxx"); //网络层的IP地址: 实际的服务器IP地址
bzero(&(serv_addr.sin_zero),8); //保留的8字节置零
//2.发起对服务器的连接信息
//三次握手,需要将sockaddr_in类型的数据结构强制转换为sockaddr
if((connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))) < 0) {
perror("connect failed!");
exit(1);
}
printf("connect successful! \n");
//3.发送消息给服务器端
if((sendbytes = send(sockfd,"hello",5,0)) < 0) {
perror("send");
exit(1);
}
printf("send successful! %d \n",sendbytes);
//4.关闭
close(sockfd);
}
以上是在 Linux(Ubuntu 18.04) 实现简单的TCP客户端与服务器的实例。
标签:sockaddr,socket,int,TCP,C语言,addr,Linux,sockfd,struct 来源: https://blog.csdn.net/weixin_48503637/article/details/122717482