socket 网络编程
主要内容:
(1)什么是socket
(2)IPv4套接口地址结构
(3)网络字节序
(4)字节序转换函数库
(5)地址装换函数
(6)套接字类型
一,(1)什么是socket
第一点:socket可以看成是用户进程与内核网络协议栈的编程结构
第二点:socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信
二,(2)IPv4套接口地址结构(结构体成员变量 和 解析)
通用的结构体:
struct sockaddr{
uint8_t sin_len;
sa_family_t sin_family;
char sa_data[14];
}
sin_len: 整个sockaddr结构体的长度
sin_family:指定该地址家族
sa_data : 由sin_family 决定他的形式
三,(3)网络字节序
大小端的引入
数据在电脑中会以两种形式存储在内存中
例如:
网络字节序: 大端字节序
当A电脑和B电脑进行网络通信的时候,第一点需要解决的问题就是大小端问题
第一步:将A电脑发送的数据------->装换为网络字节序------------->装换为B电脑本身的数据--------->传递给B电脑
以下是相关的API:
uint32_t htonl(uint32_t hostlong);
uint16_t htons (uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
上面分别是本机字节序到网络字节序之间的相互装换关系的API
四,套接字的类型
1.流式套接字(SOCK_STREAM) TCP
提供面向连接的,可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接受
2.数据报式套接字(SOCK_DGRAM)
提供无连接服务,不提供无错保证,数据可能丢失或者重复,并且接受顺序混乱
3.原始套接字(SOCK_RAW) 暂时不知道具体含义
下面代码实现的功能:
客户c代码
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
// listenfd = socket(AF_INET, SOCK_STREAM, 0)
ERR_EXIT("socket error");
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
/* inet_aton("127.0.0.1", &servaddr.sin_addr); */
if (connect(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("connect error");
struct sockaddr_in localaddr;
char cli_ip[20];
socklen_t local_len = sizeof(localaddr);
memset(&localaddr, 0, sizeof(localaddr));
if( getsockname(sock,(struct sockaddr *)&localaddr,&local_len) != 0 )
ERR_EXIT("getsockname error");
inet_ntop(AF_INET, &localaddr.sin_addr, cli_ip, sizeof(cli_ip));
printf("host %s:%d\n", cli_ip, ntohs(localaddr.sin_port));
char sendbuf[1024] = {0};
char recvbuf[1024] = {0};
while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
write(sock, sendbuf, strlen(sendbuf));
read(sock, recvbuf, sizeof(recvbuf));
fputs(recvbuf, stdout);
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
}
close(sock);
return 0;
}
服务器 c代码
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while (0)
int main(void)
{
int listenfd; //被动套接字(文件描述符),即只可以accept
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
// listenfd = socket(AF_INET, SOCK_STREAM, 0)
ERR_EXIT("socket error");
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/* servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
/* inet_aton("127.0.0.1", &servaddr.sin_addr); */
int on = 1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
ERR_EXIT("setsockopt error");
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("bind error");
if (listen(listenfd, SOMAXCONN) < 0) //listen应在socket和bind之后,而在accept之前
ERR_EXIT("listen error");
struct sockaddr_in peeraddr; //传出参数
socklen_t peerlen = sizeof(peeraddr); //传入传出参数,必须有初始值
int conn; // 已连接套接字(变为主动套接字,即可以主动connect)
if ((conn = accept(listenfd, (struct sockaddr *)&peeraddr, &peerlen)) < 0)
ERR_EXIT("accept error");
printf("recv connect ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
struct sockaddr_in localaddr;
char serv_ip[20];
socklen_t local_len = sizeof(localaddr);
memset(&localaddr, 0, sizeof(localaddr));
if( getsockname(conn,(struct sockaddr *)&localaddr,&local_len) != 0 )
ERR_EXIT("getsockname error");
inet_ntop(AF_INET, &localaddr.sin_addr, serv_ip, sizeof(serv_ip));
printf("host %s:%d\n", serv_ip, ntohs(localaddr.sin_port));
char recvbuf[1024];
while (1)
{
memset(recvbuf, 0, sizeof(recvbuf));
int ret = read(conn, recvbuf, sizeof(recvbuf));
fputs(recvbuf, stdout);
write(conn, recvbuf, ret);
}
close(conn);
close(listenfd);
return 0;
}
makefile
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
#这个名字必须和.c文件的名字一一对应起来,才能利用makefile相关的默认规则输出链接的可执行文件
BIN=client serve
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o $(BIN)