网编(1):Linux和Windows的套接字

版权声明:转载请声明 https://blog.csdn.net/qq_40732350/article/details/88812014

1 基于Linux的套接字相关函数及示例

socket函数

#include <sys/socket.h>
int socket(int domain, int type, int protocol);

bind函数

#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);

listen 函数

#include <sys/socket.h>
int listen(int sockfd, int backlog);

accept函数

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

第一步: 调用socket函数创建套接字。
第二步: 调用bind函数分配IP地址和端口号。
第三步: 调用listen函数转为可接收请求状态。
第四步 :调用accept函数受理连接请求。

服务器端 ( server )实例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int serv_sock;
	int clnt_sock;

	struct sockaddr_in serv_addr;
	struct sockaddr_in clnt_addr;
	socklen_t clnt_addr_size;

	char message[]="Hello World!";

	if(argc!=2)
	{
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET, SOCK_STREAM, 0);//第一步
	if(serv_sock == -1)
		error_handling("socket() error" ) ;

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(atoi(argv[1]));

	if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))== -1)//第二步
	error_handling("bind() error");

	if(listen(serv_sock, 5) == -1)//第三步
		error_handling("listen() error");
	clnt_addr_size = sizeof(clnt_addr);
	clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);//第四步
	if(clnt_sock == -1)
		error_handling("accept () error");

	write(clnt_sock, message, sizeof(message));
	close(clnt_sock);
	close(serv_sock);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

客户端实例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);

int main(int argc, char* argv[])
{
	int sock;
	struct sockaddr_in serv_addr;
	char message[30];
	int str_len;

	if(argc!=3)
	{
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}

	sock = socket(PF_INET, SOCK_STREAM, 0);
	if(sock == -1)
	error_handling("socket() error");

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
	serv_addr.sin_port=htons(atoi(argv[2]));

	if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
		error_handling("connect() error I");

	str_len = read(sock, message, sizeof(message)-1);
	if(str_len==-1)
		error_handling("read() error!");

	printf("Message from server: %s \n", message);
	close(sock);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

运行:——————————

//服务器
$ gcc client.c -o b.out
$ ./a.out 9190

//客服端
$ ./b.out 192.168.1.141 9190
Message from server: Hello World!

2 基于Windows的套接字相关函数及示例

添加库:

四个主要函数:

#include <winsock2.h>
SOCKET socket(int af , int type, int pr otocol);

#include <winsock2.h>
int bind(SOCKET s, const struct sockaddr * name, int namelen);

#include <winsock2.h>
int listen(SOCKET s,int backlog);

#include <winsock2.h>
SOCKET accept(SOCKET s, struct sockaddr * addr, int * addrlen);

下列函数与Linux的 connect函数相同,调用其从客户端发送连接请求 。

#include <winsock2.h>
int connect(SOCKET s, const struct sockaddr * name, int namelen);

最后这个函数在关闭套接字时调用 。 Linux中,关闭文件和套接字时都会调用close函数;而Windows中有专门用来关闭套接字的函数 。

#include <winsock2.h>
int closesoc ket(SOCKET s);

Windows 中的文件句柄和套接字句柄
Linux 内部也将套接字当作文件,因 此,不管创建文件还是套接字都返回文件描述符 。 之前也通过示例介绍了文件描述符返回及编号的过程 。 Windows中通过调用系统函数创建文件时,返回”句柄" (handle ), 换言之, Windows中的句柄相当千Linux中的文件描述符 。 只不过Windows中要区分文件句柄和套接字句柄 。 虽然都称为"句柄",但不像Linux那样完全一致 。 文件句柄相关函数与套接字句柄相关函数是有区别 的 ,这一点不同于Linux文件描述符 。
既然对句柄有了一定理解,接下来再观察基于Windows的套接字相关函数 , 这将加深各位对SOCKET类型的参数和返回值的理解 。 的确!这就是为了保存套接字句柄整型值的新数据类型,它由 typede伊明定义 。 回顾socket 、 Listen和accept等套接字相关函数,则更能体会到与Linux中套接字相关函数的相似性 。
有些程序员可能会问:“既然Winsock是以UNIX 、 Linux系列的BSD套接字为原型设计的 ,为什么不照搬过来,而是存在一定差异呢?”有人认为这是微软为了防止UNIX 、 Linux服务器端直接移植到Windows而故意为之。 从网络程序移植性角度上看,这也是可以理解的 。 但我有不同意见 。 从本质上说,两种操作系统内核结构上存在巨大差异, 而依赖于操作系统的代码实现风格也不尽相同 , 连Windows程序员给变量命名的方式也不同千Linux程序员 。 从各方面考虑,保持这种差异性就显得比较自然 。 因此我个人认为, Windows套接字与BSD系列的套接字编程方式有所不同是为了保持这种自然差异性 。

服务器实例代码:————————————————————

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hServSock, hClntSock;
	SOCKADDR_IN servAddr, clntAddr;
	int szClntAddr;
	char message[] = "Hello World!";
	if (argc = 2)
	{
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		ErrorHandling("WSAStartup() error!");

	hServSock = socket(PF_INET, SOCK_STREAM, 0);//第一步
	if (hServSock == INVALID_SOCKET)
		ErrorHandling("socket() error");

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servAddr.sin_port = htons(atoi(argv[1]));

	if (bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)//第二步
		ErrorHandling("bind() error");

	if (listen(hServSock, 5) == SOCKET_ERROR)//第三步
		ErrorHandling("listen() error");

	szClntAddr = sizeof(clntAddr);
	hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &szClntAddr);//第四步
	if (hClntSock == INVALID_SOCKET)
		ErrorHandling("accept() error");

	send(hClntSock, message, sizeof(message), 0);
	closesocket(hClntSock);
	closesocket(hServSock);
	WSACleanup();
	return 0;
}

void ErrorHandling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

客户端实例代码:————————————————————

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <minwindef.h>
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hSocket;
	SOCKADDR_IN servAddr;

	char message[30];
	int strlen;
	if (argc != 3)
	{
		printf("Usage : %s <IP> <port>\n", argv[0]);
		exit(1);
	}

	if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
		ErrorHandling("WSAStartup() error I");

	hSocket = socket(PF_INET, SOCK_STREAM, 0);//第一步
	if (hSocket == INVALID_SOCKET)
		ErrorHandling("socket() error");

	memset(&servAddr, 0, sizeof(servAddr));
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.s_addr = inet_addr(argv[1]);
	servAddr.sin_port = htons(atoi(argv[2]));

	if (connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)//第二步
		ErrorHandling("connect() error! ");

	strlen = recv(hSocket, message, sizeof(message) - 1, 0);
	if (strlen == -1)
		ErrorHandling("read() error! ");
	printf("Message from server: %s \n", message);

	closesocket(hSocket);
	WSACleanup();
	return 0;
}

void ErrorHandling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

运行程序:

//服务器
D:\>  server 9190

//客服端
D:\> client 192.168.43.110  9190
Message from server: Hello World!

基于Windows 的I/O函数

发送函数:

#include <winsock2.h>
int send(SOCKET s, canst char* buf, int len, int flags);
//成功时返回传输字节数,失败时返回SOCKET_ERROR。
s  //表示数据传输对象连接的套接字句柄值。
buf  //保存待传输数据的缓冲地址值。
len  //要传输的字节数。
flags  //传输数据时用到的多种选项信息。

接收函数:

#include <winsock2.h>
int recv(SOCKET s, const char* buf, int len, int flags);
//成功时返回传输字节数,失败时返回SOCKET_ERROR。
s  //表示数据传输对象连接的套接字句柄值。
buf  //保存待传输数据的缓冲地址值。
len  //要传输的字节数。
flags  //传输数据时用到的多种选项信息。

猜你喜欢

转载自blog.csdn.net/qq_40732350/article/details/88812014