一、TCP/IP协议通信原理
使用TCP套接字编程可以实现基于TCP/IP协议的面向连接的通信,它分为服务器端和客户端两部分,其主要实现过程如下图所示:
(1)连接建立:服务器调用socket()、 bind()、 listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。
(2)数据传输:建立连接后, TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。
(3)关闭连接:如果客户端没有更多的请求了,就调用close()关闭连接,就像写端关闭的管道一样,服务器的read()返回0,这样服务器就知道客户端关闭了连接,也调用close()关闭连接。注意,任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态,仍可接收对方发来的数据。
二、函数描述
(1)socket函数:为了执行网络输入输出,一个进程必须做的第一件事就是调用socket函数获得一个文件描述符。
-----------------------------------------------------------------
#include
int socket(int family,int type,int protocol);
返回:非负描述字---成功-1---失败
-----------------------------------------------------------------
(2)connect函数:当用socket建立了套接口后,可以调用connect为这个套接字指明远程端的地址;如果是字节流套接口,connect就使用三次握手建立一个连接;如果是数据报套接口,connect仅指明远程端地址,而不向它发送任何数据。
-----------------------------------------------------------------
#include
int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
返回:0---成功-1---失败
-----------------------------------------------------------------
(3)bind函数:为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,如果指定一个通配IP地址,则要等到建立连接后内核才选择一个本地IP地址。
-------------------------------------------------------------------
#include
int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen);
返回:0---成功-1---失败
-------------------------------------------------------------------
(4)listen函数:listen函数仅被TCP服务器调用,它的作用是将用sock创建的主动套接口转换成被动套接口,并等待来自客户端的连接请求。
-------------------------------------------------------------------
#include
int listen(int sockfd,int backlog);
返回:0---成功-1---失败
-------------------------------------------------------------------
(5)accept函数:accept函数由TCP服务器调用,从已完成连接队列头返回一个已完成连接,如果完成连接队列为空,则进程进入睡眠状态。
-------------------------------------------------------------------
#include
int accept(int listenfd, struct sockaddr *client, socklen_t * addrlen);
回:非负描述字---成功-1---失败
-------------------------------------------------------------------
(6)write和read函数:当服务器和客户端的连接建立起来后,就可以进行数据传输了,服务器和客户端用各自的套接字描述符进行读/写操作。因为套接字描述符也是一种文件描述符,所以可以用文件读/写函数write()和read()进行接收和发送操作。
(I)write()函数用于数据的发送。
-------------------------------------------------------------------
#include
int write(int sockfd, char *buf, int len);
返回:非负---成功-1---失败
-------------------------------------------------------------------
(II)read()函数用于数据的接收。
-------------------------------------------------------------------
#include
int read(int sockfd, char *buf, intlen);
回:非负---成功-1---失败
-------------------------------------------------------------------
三、编程步骤及代码实现
《一》服务器端
编程步骤:
(1)使用WSAStartup()函数检查系统协议栈安装情况
(2)使用socket()函数创建服务器端通信套接字
(3)使用bind()函数将创建的套接字与服务器地址绑定
(4)使用listen()函数使服务器套接字做好接收连接请求准备
(5)使用accept()接收来自客户端由connect()函数发出的连接请求
(6)根据连接请求建立连接后,使用send()函数发送数据,或者使用recv()函数接收数据
(7)使用closesocket()函数关闭套接字(可以先用shutdown()函数先关闭读写通道)
(8)最后调用WSACleanup()函数结束Winsock Sockets API
代码实现:
#include
#include
#pragma comment(lib,"ws2_32.lib")//WSAStartup、WSACleanup和SOCKET的动态库
int main()
{
//1 请求协议版本
WSADATA wsadata;
WSAStartup(MAKEWORD(2,2), &wsadata);//请求协议版本2.2
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
printf("请求协议失败\n");
return -1;
}
//2 创建socket
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (SOCKET_ERROR == serverSocket) {
printf("创建socket失败\n");
WSACleanup();
return -2;
}
printf("创建socket成功\n");
//3创建协议地址族
SOCKADDR_IN addr = { 0 };
addr.sin_family = AF_INET;//协议版本
addr.sin_addr.S_un.S_addr = inet_addr("192.168.43.3");//用自己IP
addr.sin_port = htons(10086);//0-65535 一般取10000左右
//4 绑定
int r = bind(serverSocket, (sockaddr*)&addr, sizeof addr);
if (r == -1) {
printf("绑定失败\n");
closesocket(serverSocket);
WSACleanup();
return -2;
}
printf("绑定成功\n");
//5 监听
r = listen(serverSocket, 10);//监听10个用户
if (r == -1) {
printf("监听失败\n");
closesocket(serverSocket);
WSACleanup();
return -2;
}
printf("监听成功\n");
//6 等待客户端连接 阻塞 尾声抱柱
SOCKADDR_IN cAddr = { 0 };
int len = sizeof cAddr;
SOCKET clientSocket = accept(serverSocket, (sockaddr*)&cAddr, &len);
if (SOCKET_ERROR == clientSocket) {
printf("服务器宕机了\n");
//关闭socket
closesocket(serverSocket);
//清除协议信息
WSACleanup();
return -2;
}
printf("有客户端连接到服务器:%s\n", inet_ntoa(cAddr.sin_addr));//将整数型IP地址转化为字符串
// 7 通信
char buff[1024];
while (1) {
r = recv(clientSocket, buff, 1023, NULL);
if (r > 0) {
buff[r] = 0;//添加'\0'
printf(">>%s\n", buff);
}
send(clientSocket, buf, sizeof(buf), 0);
}
return 0;
}
《二》客户端
编程步骤:
(1)使用WSAStartup()函数检查系统协议栈安装情况
(2)使用socket()函数创建客户端套接字
(3)使用connect()函数发出也服务器建立连接的请求(调用前可以不用bind()端口号,由系统自动完成)
(4)连接建立后使用send()函数发送数据,或使用recv()函数接收数据
使用closesocet()函数关闭套接字
(5)最后调用WSACleanup()函数,结束Winsock Sockets API
代码实现:
#include
#include
#include
#pragma warning(disable:4996)
#include
#include
#pragma comment(lib,"ws2_32.lib")//WSAStartup、WSACleanup和SOCKET的动态库
int main()
{
//1 请求协议版本
WSADATA wsadata;
WSAStartup(MAKEWORD(2, 2), &wsadata);//请求协议版本2.2
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
printf("请求协议失败\n");
return -1;
}
//2 创建socket
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (SOCKET_ERROR == clientSocket) {
printf("创建socket失败\n");
WSACleanup();
return -2;
}
printf("创建socket成功\n");
//3获取协议地址族
SOCKADDR_IN addr = { 0 };
addr.sin_family = AF_INET;//协议版本
addr.sin_addr.S_un.S_addr = inet_addr("192.168.43.3");//用自己IP
addr.sin_port = htons(10086);//0-65535 一般取10000左右
//4 连接服务器
int r = connect(clientSocket, (sockaddr*)&addr, sizeof addr);
if (r == -1) {
printf("连接服务器失败\n");
return -1;
}
printf("连接服务器成功\n");
//5 通信
char buf[1024];
string str;
int i;
cin >> str;
for (i = 0; i < str.length(); i++)
{
buf[i] = str[i];
}
buf[i] = '\0';
send(clientSocket, buf, sizeof(buf), 0);
// 接收数据
recv(clientSocket, buf, sizeof(buf), 0);
return 0;
}
四、运行结果
1、数据传送之前先启动服务器端,再启动客户端:
启动服务器后,服务器界面如下,此时客户端还未启动或请求连接。
2、启动客户端后,与服务器的连接建立成功,两者界面如下图:
3、连接建立成功后,开始传输字符串聊天,结果如下:
网络聊天实验完成
标签:addr,int,函数,C语言,hi,socket,服务器,hello,客户端
来源: /wenkail/p/1412.html