创建套接字
成功返回文件描述符,失败返回-1
int socket (int __domain, int __type, int __protocol) ;
__domain:套接字中使用的协议族信息
一般使用PF_INET(IPv4互联网协议族),其它协议族不常使用或尚未普及。另外,套接字中实际采用的最终协信息是通过socket 函数的第三个参数传递的。在指定的协议族范围内通过第一个参数决定第三个参数。
__type:套接字数据传输类型
1.面向连接的套接字(SOCK_STREAM)
特点:
传输过程中数据不会丢失
按序传输数据,较晚传输的数据不会先到达
套接字连接必须一一对应,发送端和接收端各有一个套接字
传输的数据不存在边界
2.面向消息的套接字(SOCK_DGRAM)
__protocol:协议的最终选择
传递前两个参数即可创建所需套接字。所以大部分情况下可以向第三个参数传递0,除非遇到以下这种情况:
“同一协议族中存在多个数据传输方式相同的协议”
数据传输方式相同,但协议不同。此时需要通过第三个参数具体指定协议信息。
下面以前面讲解的内容为基础,构建向socket函数传递的参数。首先创建满足如下要求的套接字:
“IPv4协议族中面向连接的套接字”
IPv4与网络地址系统相关。参数PF_INET指IPv4网络协议族, SOCK_STREAM是面向连接的数据传输。满足这2个条件的协议只有IPPROTO_TCP,因此可以如下调用socket函数创建套接字,这种套接字称为TCP套接字。
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
下面创建满足如下要求的套接字:
“IPv4协议族中面向消息的套接字”
SOCK_DGRAM指的是面向消息的数据传输方式,满足上述条件的协议只有IPPROTO_UDP。因此,可以如下调用socket函数创建套接字,这种套接字称为UDP套接字。
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
面向连接的套接字示例:
验证TCP套接字的如下特性:
“传输的数据不存在边界”
为验证这一点,需要让write函数的调用次数不同于read函数的调用次数。因此,在服务端中分多次调用write函数发送数据,客户端调用一次read函数以接收服务器端发送的全部数据。
Windows平台客户端,client.cpp
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <winsock2.h>
#include <iostream>
#include <string>
#pragma warning(disable:4996)
#pragma comment(lib,"ws2_32.lib")
using std::cout; using std::cerr; using std::endl; using std::string; using std::cin;
#define BUFFSIZE 2048
#define SERVER_IP "127.0.0.1" //"101.43.87.248" // 指定服务端的IP
#define SERVER_PORT 9190 // 指定服务端的port
int main()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2),&wsaData) != 0)
cout << "WSAStartup() error" << endl;
SOCKET hSocket = socket(PF_INET,SOCK_STREAM,0);
if(hSocket == INVALID_SOCKET)
cout << "socket() error" << endl;
SOCKADDR_IN serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
serv_addr.sin_port = htons(SERVER_PORT);
//调用 connect 函数向服务器发送连接请求
if (connect(hSocket, (SOCKADDR*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
cout << "connect() error" << endl;
char message[30] = { 0 };
if (int strLen = recv(hSocket, &message, 30, 0))
{
if (strLen == -1)
{
cout << "recv() error" << endl;
}
else
{
message[strlen]=0;
cout << message << endl;
}
}
closesocket(hSocket);
WSACleanup();
cin.get();
return 0;
}
Linux平台服务器端,server.cpp
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include <iostream>
#include <string>
using std::cout; using std::cerr; using std::endl; using std::string; using std::cin;
#define DEFAULT_PORT 9190 // 指定端口为 9190
#define LOG_BUFFSIZE 65536
int main()
{
//调用 socket 函数创建套接字, serv_sock用于存放套接字,即ip和端口
int serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1)
cout << "Create socket error" << errno << ": " << strerror(errno) << endl;
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //0.0.0.0
serv_addr.sin_port = htons(DEFAULT_PORT);
//调用 bind 函数分配ip地址和端口号
if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
cout << "Bind error" << errno << ": " << strerror(errno) << endl;
//调用 listen 函数将套接字转为可接受连接状态
if (listen(serv_sock, 5) == -1)
cout << "Listen error" << errno << ": " << strerror(errno) << endl;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
//调用 accept 函数受理连接请求。如果在没有连接请求的情况下调用该函数,则不会返回,直到有连接请求为止
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
if (clnt_sock == -1)
cout << "Accept error" << errno << ": " << strerror(errno) << endl;
char message[] = "hello world";
write(clnt_sock, message, sizeof(message));//1
write(clnt_sock, message, sizeof(message));//2
close(clnt_sock);
close(serv_sock);
return 0;
}