网络编程 Day2
- 套接字socket
- 基于TCP通信的流程
- 服务器端
- 客户端
- TCP通信API
- 基于UDP通信的流程
- 服务器端
- 客户端
- 作业
套接字socket
- socket套接字本质是一个特殊的文件,在原始的Linux中,它和管道,消息队列,共享内存,信号等一样,都只能进行主机内的通信
- 随着历史的发展,有了TCP/IP协议族的出现,使得socket套接字可以通过网卡,与外部主机进行通信
- socket函数会生成一个文件描述符,不同主机内的进程都可以对该文件描述符进程读写
基于TCP通信的流程
服务器端
- socket:创建原始套接字
- bind:将原始套接字与主机IP绑定(该服务器的身份,服务器以主机的身份通信)
- listen:将原始套接字设置为监听状态
- accept:接收客户端的连接,获取客户端信息,并生成新的套接字描述符用于与客户端通信。
- 发送信息,接收信息,关闭
客户端
- socket:创建套接字
- bind(可选):客户端绑定IP和端口号,绑定后客户端将会使用绑定的IP端口号来访问服务器,如果不绑定系统将会自动分配IP和端口号
- connect:向服务器发送连接请求
- 发送信息,接收信息,关闭
TCP通信API
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建socket套接字描述符
参数1:
AF_UNIX,
AF_LOCAL:本地通信模式
AF_INET IPv4 通信
AF_INET6 IPv6 通信
参数2传输层通信协议:
SOCK_STREAM:TCP通信协议
SOCK_DGRAM:UDP通信协议
参数3:如果参数2指定了TCP或者UDP具体通信协议,参数3可以省略,如果没有指定需要加上参数3.
返回值:成功返回套接字描述符,失败返回-1,并置位错误码。
eg:socket(AF_INET,SOCK_STREAM,0)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
参数1:套接字描述符
参数2;
struct sockaddr_in {
sa_family_t sin_family; /* IPV4通信协议 */
in_port_t sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP号(IP地址转化的IP号) */
};
参数2结构体第三个成员还是结构体如下:
struct in_addr {
uint32_t s_addr; /* IP地址在网络的形式 */
};
参数3:参数2的结构体大小。
返回值:成功返回0,失败返回-1,并置位错误码。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:监听客户端连接请求
参数1:套接字描述符
参数2:监听的最大数量是128。
返回值:成功返回0,失败返回-1,并置位错误码。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:接收连接请求,并记录连接者的信息(阻塞函数)
参数1:套接字描述符
参数2:存储连接者的详细信息。
参数3:参数2的大小。
返回值:成功返回新的描述符,失败返回-1,并置位错误码。
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
功能:与服务器建立连接的函数
参数1:套接字描述符
参数2:需要连接的服务器具体信息(IP,端口号,通信域等)
参数3:参数2的大小。
返回值:成功返回0,失败返回-1,并置位错误码
基于UDP通信的流程
服务器端
- 创建套接字
- 绑定主机IP和端口号
- 收发信息
客户端
- 创建套接字
- 收发信息
作业
使用UDP和connect函数实现一对一通信
//服务器
#include <myhead.h>
#define IP "192.168.209.241"
#define PORT 6666
int main(int argc, const char *argv[])
{
//创建UDP套接字
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == socketfd)
{
perror("socket");
return -1;
}
//绑定
struct sockaddr_in server = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = inet_addr(IP)
};
if (-1 == bind(socketfd, (struct sockaddr *)&server, sizeof(server)))
{
perror("bind");
return -1;
}
//收发信息
struct sockaddr_in client;
int client_len = sizeof(client);
int flag = 1;
char buff[1024] = "";
while (1)
{
//记录首个发送信息的客户端并连接
while (flag)
{
recvfrom(socketfd,buff,sizeof(buff),0,(struct sockaddr *)&client, &client_len);
printf("收到来自%s:%d的信息:%s\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port), buff);
if (-1 == connect(socketfd, (struct sockaddr *)&client, client_len))
{
perror("connect");
return -1;
}
flag = 0;
}
strcat(buff,"copy");
send(socketfd, buff, strlen(buff), 0);
bzero(buff, sizeof(buff));
recv(socketfd, buff, sizeof(buff), 0);
printf("收到来自%s:%d的信息:%s\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port), buff);
bzero(buff, sizeof(buff));
}
return 0;
}
//客户端
#include <myhead.h>
#define IP "192.168.209.241"
#define PORT 6666
int main(int argc, const char *argv[])
{
//创建UDP套接字
int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if (socketfd == -1)
{
perror("socket");
return -1;
}
//连接服务器
struct sockaddr_in server = {
.sin_family = AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr = inet_addr(IP)
};
if (-1 == connect(socketfd, (struct sockaddr *)&server, sizeof(server)))
{
perror("connect");
return -1;
}
//收发信息,因为使用了connect连接了服务器,所以可以直接使用send和recv函数直接操作
char buff[1024] = "";
while (1)
{
fgets(buff, sizeof(buff), stdin);
buff[strlen(buff)-1] = '\0';
// sendto(socketfd,buff,sizeof(buff),0,(struct sockaddr *)&server, sizeof(server));
send(socketfd,buff, strlen(buff),0);
bzero(buff,sizeof(buff));
// recvfrom(socketfd,buff,sizeof(buff),0,NULL,NULL);
recv(socketfd,buff, sizeof(buff), 0);
printf("收到服务器信息:%s\n", buff);
}
return 0;
}