一、理解socket
-
套接字 应用进程跨网络的通信需要用到socket套接字实现,在TCP/IP协议中,“IP地址+TCP或UDP“唯一的标识了网络通信中的一个进程,这也就叫做一个套接字。 -
套接字产生 引入套接字就不得不介绍一下**系统调用**和**应用编程接口**。
当我们有一些特定的应用需要互联网的支持,但这些应用又不能直接使用已经标准化的互联网协议,那么我们应当做哪些工作?
大多数操作使用**系统调用**(system call)的机制在应用程序和操作系统之间传递控制权。
当某个应用进程启动系统调用时,控制权就从应用进程传递给系统调用接口。此接口再把控制权传递给操作系统,操作系统把这个调用转给某个内部过程,并执行所请求的操作。内部操作一旦执行完毕,控制权就又通过系统调用接口返回给应用进程。因此,系统调用接口实际上就是应用进程的控制权和操作系统的控制权进行转换的一个接口。由于应用进程在使用系统调用接口之前要编写一些程序,特别是要设置系统调用中的许多参数,因此这种系统调用接口又称为应用编程接口API(Application Programming Interface) 。现在的TCP/IP协议软件已经驻留在操作系统中了。由于TCP/IP协议族被设计成能够运行在多种操作系统的环境中,因此TCP/IP标准没有规定应用程序与TCP/IP协议软件如何接口的细节,而是允许系统设计者能够选择有关API的具体实现细节。目前只有几种可供应用程序使用TCP/IP的应用编程接口API,其中最著名的就是美国加利福尼亚大学伯克利分校为Berkeley UNIX操作系统定义的一种API,它又称为套接字接口(socket interface)。微软在其操作系统中采用了套接字接口API,形成了一个稍有不同的API,并称之为Windows Socket,简称WinSock。(小编在这里只用了微软的)
1. 3.调用socket创建套接字
- 以使用TCP服务为例的系统调用
4.1.建立连接阶段 当套接字被创建后,它的端口号和地址都是空的,进程需要调用Bind来指明套接字本地地址(本地端口号和IP地址)。在服务器端Bind就是把熟知端口号本地IP地址填写到自己创建的套接字里。 服务器调用Bind后还必须调用Listen把套接字设置为被动模式,随时接受客户端服务请求。 服务器紧接着调用accept,把远程客户端发送的连接请求提取出来。此处可以并发,因为处理了多个连接请求。 4.2.数据传送阶段 客户和服务器都在TCP连接上使用send系统调用传送数据,使用recv系统调用接收数据。而各函数的定义可在编译器中具体查看。 4.3.连接释放 客户或服务器结束使用套接字,就把套接字撤销。调用close。
二、具体实现 实现环境:vs2019,c语言,
#include<stdio.h>
#include<windows.h>
#pragma comment(lib,"ws2_32.lib")
SOCKADDR_IN cAddr = { 0 };
int len = sizeof cAddr;
SOCKET clientSocket[1024];
int count = 0;
void tonxin(int idx)
{
char buff[1024];
int r;
while (1)
{
r = recv(clientSocket[idx], buff, 1023, NULL);
if (r > 0)
{
buff[r] = 0;
printf("%d:%s\n", idx, buff);
for (int i = 0; i < count; i++)
{
send(clientSocket[i], buff, strlen(buff), NULL);
}
}
}
}
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("请求协议版本失败!\n");
return -1;
}
printf("请求协议成功!\n");
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (SOCKET_ERROR == serverSocket)
{
printf("创建socket失败!\n");
WSACleanup();
return -2;
}
printf("创建socket成功!\n");
SOCKADDR_IN addr = { 0 };
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr("");
addr.sin_port = htons(5000);
int r = bind(serverSocket, (SOCKADDR*)&addr, sizeof addr);
if (-1 == r)
{
printf("bind失败!\n");
closesocket(serverSocket);
WSACleanup();
return -3;
}
printf("bind成功!\n");
r = listen(serverSocket, 10);
if (-1 == r)
{
printf("listen失败!\n");
closesocket(serverSocket);
WSACleanup();
return -2;
}
printf("listen成功!\n");
while (1)
{
clientSocket[count] = accept(serverSocket, (SOCKADDR*)&cAddr, &len);
if (SOCKET_ERROR == clientSocket[count])
{
printf("服务器宕机!\n");
closesocket(serverSocket);
WSACleanup();
return -2;
}
printf("有客户端连接到服务器了:%s!\n", inet_ntoa(cAddr.sin_addr));
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)tonxin, (char*)count, NULL, NULL);
count++;
}
char buff[1024];
while (1)
{
r = recv(clientSocket[count], buff, 1023, NULL);
if (r > 0)
{
buff[r] = 0;
printf(">>%s\n", buff);
}
}
return 0;
}
- 客户端cilent.c
#include<stdio.h>
#include<windows.h>
#include<graphics.h>
#pragma comment(lib,"ws2_32.lib")
SOCKET cilentSocket;
HWND hWnd;
int count = 0;
void receive()
{
char recvBuff[1024];
int r;
while (1)
{
r = recv(cilentSocket, recvBuff, 1023, NULL);
if (r > 0)
{
recvBuff[r] = 0;
outtextxy(0, count * 20, recvBuff);
count++;
}
}
}
int main()
{
hWnd = initgraph(300, 300, SHOWCONSOLE);
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("请求协议版本失败\n");
return -1;
}
printf("请求协议版本成功\n");
cilentSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (cilentSocket == SOCKET_ERROR)
{
printf("创建socket失败\n");
WSACleanup();
return -1;
}
printf("创建socket成功\n");
SOCKADDR_IN addr = { 0 };
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = inet_addr("");
addr.sin_port = htons(5000);
int r = connect(cilentSocket, (SOCKADDR*)&addr, sizeof addr);
if (r == -1)
{
printf("连接服务器失败\n");
return -1;
}
printf("连接服务器成功\n");
char buff[1024];
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)receive, NULL, NULL, NULL);
while (1)
{
memset(buff, 0, 1024);
printf("你想说什么?\n");
scanf_s("%s", buff);
r = send(cilentSocket, buff, strlen(buff), NULL);
}
return 0;
}
cilent端的代码在vs2019中有个错误会报出来,不过不用担心,这个错误不是代码错误,而是环境问题,就是在vs2003以后的版本字符集默认是Unicode,只需要在”项目“----”属性“----”高级“----”字符集(Unicode)“改为----”多字节字符集“ 即可通过。
运行结果截图类似于如下: 图中表明服务器端确实连到了其它客户端,该图中左边的IP地址填入的应为服务器端IP(小编填的是自己的电脑连的宽度网);而客户端(最好找台另外的电脑,比如说室友的,让他的电脑在vs2019运行cilent.c,你就会很明显的感受到初学时连到情况了。)的IP是客户端自己本机的IP地址。
以上便是小编第一次成功实现socket的情况,初学较浅,望君善待之。
|