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 //传输数据时用到的多种选项信息。