一、Socket
- Socket 在 Linux 环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。
- 在 TCP/IP 协议中,“ IP 地址+ TCP 或 UDP 端口号 ” 唯一标识网络通讯中的一个进程。“ IP 地址+ 端口号 ”就对应一个 socket。欲建立连接的两个进程各有一个 socket 来标识,那么这两个 socket 组成的 socket pair 就唯一标识一个连接。因此可以用 socket 来描述网络连接的一对一关系。
- 在网络通信中,套接字一定是成对出现的。
如图为套接字通信原理:
TCP通信socket模型创建流程图:
二、使用的函数
- int socket( int domain, int type, int protocal);
- domain:
- AF_INET 使用 IPv4 的地址
- AF_INET6 使用 IPv6 的地址
- type:
- SOCK_STREAM 这协议是按照顺序的、可靠的、数据完整的基于字节流的连接。使用 TCP 来进行传输。
- SOCK_DGRAM 是无连接的、固定长度的传输调用。是不可靠的,使用UDP。
- protocol:
- 传 0 表示使用默认协议。
- 返回值:
- 成功:返回指向新创建的socket 的文件描述符
- 失败:返回 -1
- domain:
- int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- sockfd:
- socket 文件描述符
- addr:
- 构造出 IP 地址加端口号
- addrlen:
- sizeof(addr) 长度
- 返回值:0/ -1
- sockfd:
- int listen( int sockfd, int backlog);
- sockfd:
- socket 文件描述符
- backlog:
- 排队建立3次握手队列和刚刚建立3次握手队列的连接数和
- sockfd:
-
int accept( int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- sockdf:
- socket 文件描述符
- addr:
- 传出参数,返回链接客户端地址信息,含 IP 地址和端口号
- addrlen:
- 传入传出参数, 传入sizeof(addr)大小, 函数返回时返回真正接收到地址结构体的大小。
- 返回值:
- 成功返回一个新的 socket 文件描述符,用于和客户端通信,失败 -1。
- sockdf:
-
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- sockdf:
- socket 文件描述符
- addr:
- 传入参数,指定服务器端地址信息,含 IP 地址和端口号。
- addrlen:
- 传入参数,传入 sizeof(addr) 大小
- 返回值: 0/ -1
- sockdf:
三、代码
先运行服务端,再运行客户端
服务端
#include<stdio.h>
#include<ctype.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#define SERV_PORT 9527
void sys_err(const char *str){
perror(str);
exit(1);
}
int main(int argc, char *argv[]){
int lfd = 0, cfd = 0;
int ret;
char buf[BUFSIZ], client_IP[1024];
struct sockaddr_in serv_addr, clit_addr;
socklen_t clit_addr_len;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd == -1){
sys_err("socket error");
}
bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
listen(lfd, 128);
clit_addr_len = sizeof(clit_addr);
cfd = accept(lfd, (struct sockaddr *)&clit_addr, &clit_addr_len);
if(cfd == -1){
sys_err("accept error");
}
printf("client ip:%s port:%d\n", inet_ntop(AF_INET, &clit_addr.sin_addr, client_IP, sizeof(client_IP)), ntohs(clit_addr.sin_port));
while(1){
ret = read(cfd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, ret);
for(int i=0; i<ret; i++){
buf[i] = toupper(buf[i]);
}
write(cfd, buf, ret);
}
close(lfd);
close(cfd);
return 0;
}
客户端
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#define SERV_PORT 9527
void sys_err(const char *str){
perror(str);
exit(1);
}
int main(int argc, char *argv[]){
int cfd;
int conter = 0;
char buf[BUFSIZ];
struct sockaddr_in serv_addr; //服务器地址结构
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr);
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd == -1){
sys_err("socket error");
}
int ret = connect(cfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(ret != 0){
sys_err("connect err");
}
while(--conter){
write(cfd, "hello\n", 6);
sleep(1);
ret = read(cfd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, ret);
}
close(cfd);
return 0;
}
效果