目录
TCP通信实现过程
1、socket函数与通信域
socket函数
参 数
bind函数 与 通信结构体
bind函数
参数
通信地址族与同届结构体
通用地址族结构体
IPV4地址族结构体
listen函数与accept函数
listen函数
accept函数
参 数
作 用
要实现进程间的通信必备:IP地址、端口号、协议(TCP or UDP)
TCP通信实现过程
1、建立连接;(socket, bind, listen, accept)
2、数据传输;(read / write,recv / send )
3、连接释放;(close, shutdown)
1、socket函数与通信域
socket函数
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参 数
-domain :指定通信域(通信地址族);
通信地址族种类:AF_INET : 使用ipv4互联网协议 AF_INET6 : 使用ipv6互联网协议, 等;
这里使用Ipv4;
-type :指定套接字类型;
TCP唯一对应流式套接字,所以选择SOCK_STREAM;
-protocol :指定协议;
流式套接字唯一对应 TCP ,所以不需要指定协议设置为 0 即可;
bind函数 与 通信结构体
bind函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数
-sockfd :socket函数生成的套接字;
-addr :通信结构体;
-addrlen :通信结构体长度;
通信地址族与同届结构体
通信地址族用于获取IP地址和端口号
通用地址族结构体
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
IPV4地址族结构体
struct sockaddr_in {
sa_family sin_family; /*地址族:AF_INET*/
in_port_t sin_port; /*网络字节序的端口号*/
struct in_addr sin_addr; /*IP地址结构体*/
};
/*IP地址结构体*/
struct in_addr {
uint32_t s_addr; /*网络字节序的IP地址*/
};
listen函数与accept函数
listen函数
/*监听套接字*/
int listen(int sockfd, int backlog);
accept函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参 数
-sockfd : 函数socket生成的套接字;
-addr : 客户端的地址族信息;
(struct socketaddr --->是对应客户端的结构体)
-addrlen : 地址族结构体的长度;
作 用
accept() 函数用于套接字编程,其作用是接受来自客户端的连接请求。具体而言,它用于通信的服务器端接受客户端通过套接字终点发出的连接请求。
当客户端向服务器发起连接时,它会向服务器发送一个 SYN 包。服务器随后会响应一个 SYN-ACK 包。当客户端从服务器接收到 SYN-ACK 包时,它会向服务器发送一个 ACK 包以完成三次握手并建立连接。一旦建立了连接,服务器就会调用 `accept()` 函数来接受客户端的连接请求。
`accept()` 函数会阻塞执行,直到收到新的客户端连接。一旦收到连接,`accept()` 函数将返回一个新的套接字描述符,该描述符将用于与客户端进行通信。这个新的套接字描述符表示一个特定客户端已连接到服务器的单独套接字终点。服务器随后可以使用新的套接字描述符来与客户端通信,以实现建立的连接。
inet_aton() 函数
是一个 C 库函数,用于将一个字符串形式的 IPv4 地址转换为一个网络字节序的二进制地址,然后存储到指定的网络地址结构体中。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <signal.h>
#include <sys/wait.h>
#define BACKLOG 5
void SigHandle(int sig)
{
if(sig == SIGCHLD){
printf("clinet exited\n");
wait(NULL);
}
}
void ClinetHandle(int newfd);
int main(int argc, char *argv[])
{
int fd ,newfd;
struct sockaddr_in addr, clinet_addr;
socklen_t addrlen = sizeof(clinet_addr);
struct sigaction act;
act.sa_handler = SigHandle;
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
sigaction(SIGCHLD, &act, NULL);
pid_t pid;
/*参数检查*/
if(argc < 3)
{
fprintf(stderr, "%s<addr><port>\n", argv[0]);
exit(0);
}
/*创建套接字*/
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0) {
perror("socket");
exit(0);
}
/*地址快速重用*/
int flag = 1, len = sizeof(int);
if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1)
{
perror("setsockopt");
exit(1);
}
/*设置通信地址族结构体*/
addr.sin_family = AF_INET; //网络协议;
addr.sin_port = htons( atoi(argv[2]) ); //端口号;
if (inet_aton(argv[1], &addr.sin_addr) == 0) {
fprintf(stderr, "Invalid address\n");
exit(EXIT_FAILURE);
}
/*绑定通信地址组*/
if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
perror("bind");
exit(0);
}
/*设置套接字为监听模式*/
if(listen(fd, BACKLOG) == -1)
{
perror("listen");
exit(0);
}
/*接收客户端连接请求*/
while(1)
{
newfd = accept(fd, (struct sockaddr *)&clinet_addr, &addrlen);
if(newfd < 0)
{
perror("accept");
exit(0);
}
printf("addr:%s port:%d\n", inet_ntoa(clinet_addr.sin_addr),
ntohs(clinet_addr.sin_port));
/*多进程并发*/
if((pid = fork()) < 0)
{
perror("fork");
exit(0);
}else if (pid == 0){
close(fd);
ClinetHandle(newfd);
exit(0);
}else
close(newfd);
}
/*关闭套接字文件描述符*/
close(fd);
return 0;
}
void ClinetHandle(int newfd)
{
int ret;
char buf[BUFSIZ] = {};
/*循环接收客户端信息*/
while(1)
{
memset(buf, 0, BUFSIZ);
ret = read(newfd, buf, BUFSIZ);
if(ret < 0)
{
perror("read");
exit(0);
}else if(ret == 0){
break;
}else{
printf("buf=%s\n", buf);
}
}
close(newfd);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#define BACKLOG 5
int main(int argc, char *argv[])
{
int fd;
char buf[BUFSIZ] = {};
struct sockaddr_in addr;
/*参数检查*/
if(argc < 3)
{
fprintf(stderr, "%s<addr><port>\n", argv[0]);
exit(0);
}
/*创建套接字*/
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0) {
perror("socket");
exit(0);
}
/*设置通信地址族结构体*/
addr.sin_family = AF_INET; //网络协议;
addr.sin_port = htons( atoi(argv[2]) ); //端口号;
if (inet_aton(argv[1], &addr.sin_addr) == 0) {
fprintf(stderr, "Invalid address\n");
exit(EXIT_FAILURE);
}
/*向服务器发起连接请求*/
if( connect(fd,(struct sockaddr *)&addr, sizeof(addr)) == -1 )
{
perror("connect");
exit(0);
}
/*循环接收客户端信息*/
while(1)
{
printf("input:");
fgets(buf, BUFSIZ, stdin);
write(fd, buf, strlen(buf));
}
/*关闭套接字文件描述符*/
close(fd);
return 0;
}