服务器和客户端建立socket连接的步骤 (服务器与客户端建立socket过程)
在计算机网络中,传输层级别中的协议TCP和UDP是最常用的协议。无论哪种协议,对于数据的传输,都需要通过TCP/IP协议栈中的Socket来实现。Socket提供了一个能够使不同程序在同一台电脑或网络中互相通信的接口。本文将介绍服务器和客户端建立Socket连接的步骤。
之一步:服务器端创建Socket
在服务器端创建Socket,以便能够监听客户端传来的请求。这个过程包括以下几个步骤:
1. 创建一个Socket对象:服务器端需要通过创建一个Socket对象来对客户端的请求进行监听。创建时需要传入IP地址和端口号。
ServerSocket serverSocket = new ServerSocket(端口号);
2. 监听客户端请求:创建好Socket对象后,服务器需要通过调用accept()方法来监听客户端传来的请求。
Socket socket = serverSocket.accept();
3. 处理客户端请求:当服务器接收到一个客户端的请求后,需要对该请求进行处理。这通常包括建立和通信过程中所需要的初始参数。
以上就是服务器端创建Socket的步骤。
第二步:客户端创建Socket并连接服务器端
客户端需要通过创建Socket对象并与服务器端建立连接来发送请求和接收响应消息。这个过程包括以下几个步骤:
1. 创建一个Socket连接:客户端需要在Socket连接上设置IP地址和端口号以便连接服务器。
Socket socket = new Socket(服务器IP地址, 端口号);
2. 发送请求:客户端在与服务器端建立连接之后,需要将请求发送给服务器端。
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.print(“请求消息”);
writer.flush();
3. 接收响应消息:一旦客户端发送请求成功后,服务器端会发送响应消息给客户端。客户端需要读取响应消息来判断是否成功。
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String response = reader.readLine();
以上就是客户端创建Socket并连接服务器端的步骤。
第三步:完成通信
前两个步骤完成之后,服务器端和客户端都已经连接到了Socket上。服务器端可以监听客户端的请求,而客户端可以向服务器端发送请求并接收返回的响应消息。接下来,需要在这个连接上建立通信并传输数据。
1. 对于服务器端,需要在accept()方法里面新建一个线程,以便能够同时处理多个客户端的请求
class ServerThread extends Thread {
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// 处理客户端请求
}
}
while (true) {
try {
Socket socket = serverSocket.accept();
ServerThread serverThread = new ServerThread(socket);
serverThread.start();
} catch (IOException e) {
e.printStackTrace();
}
}
2. 对于客户端,通过获取输入输出流的方式完成数据的传输:
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream());
3. 客户端和服务器端可以通过数据流的方式完成数据的传输
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
String msg = inputStream.readUTF();
以上就是完成通信的步骤。
本文介绍了服务器和客户端建立Socket连接的步骤。首先需要在服务器端创建Socket以便能够监听客户端传来的请求;其次客户端需要通过创建Socket对象并与服务器端建立连接来发送请求和接收响应消息;最后需要在这个连接上建立通信并传输数据。对于实现一个较为复杂的网络应用,这些步骤仅仅起到了基础作用。对于高负荷、高并发、可扩展性和可靠性等方面的要求,需要更加完善和细致的实现和设计。
相关问题拓展阅读:
vbenadmin怎么socket连接
1、调用Socket类的构造函数,以服务器的指定的IP地址或指定的主机名和指定的端口号为参数,创建一个Socket流,在创建Socket流的过程中包含了向服务拍此器请求建立通讯连接的过程实现。
2、建立了客户端通讯Socket后。就可以使用Socket的方法getInputStream()和getOutputStream()来创建输入/输出流。这样,使用Socket类后,网络输入输出也转化为使用流对象的过程。
3、使用早悉输入输出流对象的相应方法读写字节流数据陆贺乎,因为流连接着通讯所用的Socket,Socket又是和服务器端建立连接的一个端点,因此数据将通过连接从服务器得到或发向服务器。这时我们就可以对字节流数据按客户端和服务器之间的协议进行处理,完成双方的通讯任务。
4、待通讯任务完毕后,我们用流对象的close方法来关闭用于网络通讯的输入输出流,在用Socket对象的close方法来关闭Socket并进行连接。
mac上怎么连接到socket
一、建立socket链接,Mac端终端使用nc命令做端口监听,oc作为客户端建立socket连接。
使用的工具及使用方法:
nc/netcat(选项)(参数)
-g:设置路由器跃程通信网关,最多设置8个;
-G:设置来源路由指向器,其数值为4的倍数;
-h:在线帮助;
-i:设置时间间隔,以便传送信息及扫描通信端口;
-l:咐巧孙使用监听模式,监控传入的资料;
-n:直接使用ip地址,而不通过域名服务器;
-o:指定文件名称,把往来传输的数据以16进制字码倾倒成该文件保存;
-p:设置本地主机使用的通信端口;
-r:指定源端口和目的端口都衡链进行随机的选择;
-s:设置本地主机送出数据包的IP地址;
-u:使用UDP传输协议;
-v:显示指令执行过程;
-w:设置等待连线的时间;
-z:使用0输入/输出模式,只在扫描通信端口时使用。
1、服务端 端口监听
nc -l 6666
2、永久监听TCP端口
nc -lk port
3、临时监听UDP
nc -lu port
4、永久监听UDP
nc -luk port
5、连接服务端
nc -v 127.0.0.1 666
6、端口扫描
nc -v -w 1 127.0.0.1 -z
客户端代码:
#import “ViewController.h”
#import
#import //inet_addr
#define connect_host @”127.0.0.1″
#define connect_port 6666
@interface ViewController (){
int clientSocket;
dispatch_queue_t queSerial;
}
@end
@implementation ViewController
– (void)viewDidLoad {
;
//button
UIButton *button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 100, 200, 30);
button.backgroundColor = ;
;
button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 150, 200, 30);
button.backgroundColor = ;
;
button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 200, 200, 30);
button.backgroundColor = ;
;
;
}
-(void)btn:(UIButton *)button{
if (
;
}else if (
UIImage *image = ;
NSData *data = UIImagePNGRepresentation(image);
NSLog(@”height:%f”,image.size.height);
NSString *dataString = initWithData:data encoding:NSUTF8StringEncoding>;
NSLog(@”%@”,dataString);
//;
}else if (
}
}
//发起socket连接
-(BOOL)initSocket{
/*
之一个参数:adress_family,协议簇 AF_INET:IPV4
第二个参数:数据格式->SOCK_STREAM(TCP)/SOCK_DGRAM(UDP)
第三个参数:protocal IPPROTO_TCP,如果为0会根据第二个参数选择合适的协议
返回值:>0成功 -1失败
*/
clientSocket = socket(AF_INET, SOCK_STREAM, 0);
NSLog(@”clientsocket:%d”,clientSocket);
if (clientSocket>0) {
NSLog(@”socket create success”);
}else{
NSLog(@”socket create error”);
}
/*
连接
之一个参数:客户端socket
第二个参数:指向数据结构,socketAddr的指针,其中包括目的端口和IP地址
第三个参数:结构体数据长度
返回值:0成功 其他错误
*/
struct sockaddr_in addr4 = {0};
addr4.sin_family = AF_INET;//ipv4
addr4.sin_len = sizeof(addr4);
addr4.sin_addr.s_addr = inet_addr(connect_host.UTF8String);
addr4.sin_port = htons(connect_port);//是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处
int flag = connect(clientSocket, (const struct sockaddr *)&addr4, sizeof(addr4));
if (!flag) {
;
}else{
clientSocket = 0;
NSLog(@”连接失败”);
}
return flag;
}
//接收消息
-(void)receiveMessage{
if (!queSerial) {
queSerial=dispatch_queue_create(“jrQueueSerial”, DISPATCH_QUEUE_SERIAL);
}
dispatch_async(queSerial, ^{
uint8_t buffer;
ssize_t recvLen = recv(self->clientSocket, buffer, sizeof(buffer), 0);
NSData *data = ;
NSString *str = initWithData:data encoding:NSUTF8StringEncoding>;
if (recvLen>0) {
NSLog(@”%@:%@”,str,);
NSLog(@”%@”,str);
;
}else{
NSLog(@”连接断开”);
self->clientSocket = 0;
}
});
}
//发送消息
-(BOOL)sendMessage:(NSString *)message{
if (clientSocket==0) {
BOOL flag = ;
if (!flag)return NO;
}
ssize_t sendLen = send(clientSocket, message.UTF8String, strlen(message.UTF8String), 0);
NSLog(@”发送消息长度:%zd”,sendLen);
return sendLen>=0;
}
@end
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
1、在终端执行命令监听端口6666:
nc -l 127.0.0.1 6666
进入等待连接状态。
2、执行以上oc代码进行socket连接:
client_socket.png
3、以上连接成功,由服务端发送一条消息,客户端接收打印如下:
client_receive.png
注:以上代码中图片发送及文本+图片发送未完成。
二、oc模拟服务端代替Mac终端命令,一步步实现服务端的三次握手及通信。主要使用方法:
1、创建一个socket:
socket(AF_INET, SOCK_STREAM, 0);
之一个参数:address_family,协议簇 AF_INET对应的IPV4;
第二个参数:数据格式可选两种SOCK_STREAM(TCP)、SOCK_DGRAM(UDP);
第三个参数:protocal IPPROTO_TCP,设置为0会根据第二个参数选择相应的协议;
返回值:sockaddr -1失败 其他成功为socket标号1、2、3、 4,指示为当前socket。
2、端口绑定:
bind(sockaddr, (const struct sockaddr *)&addr4, sizeof(addr4));
之一个参数:创建的socket描述号;
第二个参数:对应socketaddr_in(对应IPV4)的结构体包含了端口号;
第三个参数:socketaddr_in结构体长度。
3、监听端口:
listen(sockaddr, 5);
之一个参数:创建的socket标号;
第二个参数:可以排队的更大连接个数。
4、获取连接的socket标号和以上使用的标号不同
accept(sockaddr, (struct sockaddr *)&aptsockaddr, &addrLen);
之一个参数:创建的socket描述号;
第二个参数:可以排队的更大连接个数;
返回值:接收后的socket标号。
5、接收客户端消息:
recv(aptsocket, buffer, len, 0);
之一个参数:accept返回的标号理解为当前socket;
第二个参数:接收字符的缓存变量;
第三个参数:一般设置0;
返回值:接收到的数据长度。
6、发送消息:
send(aptsocket, message.UTF8String, strlen(message.UTF8String), 0);
之一个参数:accept返回的标号理解为当前socket;
第二个参数:发送的消息字符char *型数据;
第三个参数:一般设置0;
返回值:发送的数据长度。
服务端代码:
/*
ipv6
struct sockaddr_in6 addr6 = {0};
bzero(&addr6, sizeof(addr6));
addr6.sin6_len = sizeof(addr6);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(connect_port);
htons将主机的无符号短整形数转换成网络字节顺序
htonl将主机的无符号长整形数转换成网络字节顺序
*/
#import “ViewController.h”
#import
#import //inet_addr
#define connect_host @”127.0.0.1″
#define connect_port 6666
@interface ViewController ()
{
int sockaddr;//创建的socket地址
int aptsocket;//同意后返回的socket地址
dispatch_queue_t queSerial;//接收消息的队列
BOOL socket_flag;//socket标识
}
@end
@implementation ViewController
– (void)viewDidLoad {
;
//button
UIButton *button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 100, 200, 30);
button.backgroundColor = ;
;
button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 150, 200, 30);
button.backgroundColor = ;
;
button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 200, 200, 30);
button.backgroundColor = ;
;
//创建服务端socket
;
}
-(void)btn:(UIButton *)button{
if (
;
}else if (
;
}else if (
;
}
}
//初始化socket
-(BOOL)initServerSocket{
socket_flag = YES;
//1、创建一个socket
sockaddr = socket(AF_INET, SOCK_STREAM, 0);
NSLog(@”sockaddr:%d”,sockaddr);
if (sockaddr==-1) {
NSLog(@”创建失败”);
return NO;
}
//ipv4
struct sockaddr_in addr4 = {0};
//参数说明:s 要置零的数据的起始地址; n 要置零的数据字节个数。
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(connect_port);//将主机的无符号短整形数转换成网络字节顺序
addr4.sin_addr.s_addr = INADDR_ANY;//就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”
//2、端口绑定
int error = bind(sockaddr, (const struct sockaddr *)&addr4, sizeof(addr4));
if (error!=0) {
NSLog(@”绑定失败”);
return NO;
}
//3、监听端口
error = listen(sockaddr, 5);//开始监听第二个参数可以排队的更大连接个数
if (error!=0) {
NSLog(@”监听失败”);
return NO;
}
//4、轮询
if (!queSerial) {
queSerial = dispatch_queue_create(“receive_queue”, DISPATCH_QUEUE_SERIAL);
}
dispatch_async(queSerial, ^{
;
});
return YES;
}
//轮询接收消息
-(void)receiveMessage{
while (true) {
NSLog(@”currentThread:%@”,);
struct sockaddr_in aptsockaddr;
socklen_t addrLen = sizeof(aptsockaddr);
//4、获取连接的socket
aptsocket = accept(sockaddr, (struct sockaddr *)&aptsockaddr, &addrLen);
NSLog(@”aptsocket:%d”,aptsocket);
if (aptsocket != -1) {
NSLog(@”accept success address:%ss, port:%d”,inet_ntoa(aptsockaddr.sin_addr),ntohs(aptsockaddr.sin_port));
char buffer;
ssize_t recvLen;
size_t len = sizeof(buffer);
do{
//5、接收客户端的消息
recvLen = recv(aptsocket, buffer, len, 0);
NSString *str = ;
NSLog(@”receive:%@”,str);
NSLog(@”buffer:%s”,buffer);
;
}while(socket_flag);
}
close(aptsocket);
break;//链接失败后跳出轮询
}
}
//发送消息
-(BOOL)sendMessage:(NSString *)message{
if (sockaddr==0) {
NSLog(@”链接已断开”);
}
ssize_t sendLen = send(aptsocket, message.UTF8String, strlen(message.UTF8String), 0);
NSLog(@”发送消息长度:%zd”,sendLen);
return sendLen>=0;
}
//断开链接
-(void)closeSocket{
NSLog(@”关闭链接”);
socket_flag = NO;
}
@end
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
通过以上过程,对socket通信能有一个初步了解,可以利用socket通信搭建一套简单的聊天系统
Socket 原创
:48:48
yahibo
码龄7年
关注
socket又称为套接字,是应用层和传输层中间的软件抽象层,在网络中两个应用通过双向通信连接实现数据交互,向网络中另一个应用发送请求或应答其他网络请求,区别于WebSocket,socket是一组接口,WebSocket是协议。通信及关闭通信过程需要建立三次握手连接和四次挥手协议。
三次握手建立连接
之一次握手:客户端向服务端发送一个SYN(同步序列编号)包到服务端,并进入syn_send状态,等待服务器进行确认
第二次握手:服务器收到并确认客户端的SYN包,同时服务端也发送一个SYN包(SYN+ACK确认字符)包,此时服务器进入syn_recv状态
第三次握手:客户端接收到SYN+ACK包之后,向服务器发送确认包SYN+1,此时客户端与服务端进入到确立状态
完成三次握手之后两端便可以通讯了。
握手过程:
build_connect.png
有聚有散,四次挥手协议
之一次挥手:客户端向服务端发送FIN+ACK包,告诉服务端需要要结束连接
第二次挥手:服务端收到断开连接消息后,返回ACK包表示确认,确认序号为收到的序号加1,表示同意断开
第三次挥手:服务端数据传输完成后关闭客户端的连接,并向客户端发送FIN+ACK包通知客户端关闭连接
第四次挥手:客户端发送FIN+ACK包确认,FIN+1,服务端接收数据后断开连接,客户端等待一段时间,没有消息后即断开连接
完成之后TCP连接断开,以上为客户端主动发起断开连接的请求,服务器发起过程同上。
挥手过程:
bye.png
socket连接示意图:
socket.png
五层协议体系结构
framework.png
一、建立socket链接,Mac端终端使用nc命令做端口监听,oc作为客户端建立socket连接。
使用的工具及使用方法:
nc/netcat(选项)(参数)
-g:设置路由器跃程通信网关,最多设置8个;
-G:设置来源路由指向器,其数值为4的倍数;
-h:在线帮助;
-i:设置时间间隔,以便传送信息及扫描通信端口;
-l:使用监听模式,监控传入桥液的资料;
-n:直接使用ip地址,而不通过域名服务器;
-o:指定文件名称,把往来传输的数据以16进制字码倾倒成该文件保存;
-p:设置本地主机使用的通信端口;
-r:指定源端口和目的端口都进行随机的选择;
-s:设置本地主机送出数据包的IP地址;
-u:使用UDP传输协议;
-v:显示指令执行过程;
-w:设置等待连线的时间;
-z:使用0输入/输出模式,只在扫描通信端口时使用。
1、服务端 端口监听
nc -l 6666
2、永久监听TCP端口
nc -lk port
3、临时监听UDP
nc -lu port
4、永久监听UDP
nc -luk port
5、连接服务端
nc -v 127.0.0.1 666
6、端口扫描
nc -v -w 1 127.0.0.1 -z
客户端代码:
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
1、在终端执行命令监听端口6666:
nc -l 127.0.0.1 6666
进敏伍物入等待连接状态。
2、执行以上oc代码进行socket连接:
client_socket.png
3、以上连接成功,由服务端发送一条消息,客户端接收打印如下:
client_receive.png
注:以上代码中图片发送及文本+图片发送未完成。
二、oc模拟服务端代替Mac终端命令,一步步实现服务端的三次握手及通信。主要使用方法:
1、创建一个socket:
socket(AF_INET, SOCK_STREAM, 0);
之一个参数:address_family,协议簇 AF_INET对应的IPV4;
第二个参数:数据格橘悄式可选两种SOCK_STREAM(TCP)、SOCK_DGRAM(UDP);
第三个参数:protocal IPPROTO_TCP,设置为0会根据第二个参数选择相应的协议;
返回值:sockaddr -1失败 其他成功为socket标号1、2、3、 4,指示为当前socket。
2、端口绑定:
bind(sockaddr, (const struct sockaddr *)&addr4, sizeof(addr4));
之一个参数:创建的socket描述号;
第二个参数:对应socketaddr_in(对应IPV4)的结构体包含了端口号;
第三个参数:socketaddr_in结构体长度。
3、监听端口:
listen(sockaddr, 5);
之一个参数:创建的socket标号;
第二个参数:可以排队的更大连接个数。
4、获取连接的socket标号和以上使用的标号不同
accept(sockaddr, (struct sockaddr *)&aptsockaddr, &addrLen);
之一个参数:创建的socket描述号;
第二个参数:可以排队的更大连接个数;
返回值:接收后的socket标号。
5、接收客户端消息:
recv(aptsocket, buffer, len, 0);
之一个参数:accept返回的标号理解为当前socket;
第二个参数:接收字符的缓存变量;
第三个参数:一般设置0;
返回值:接收到的数据长度。
6、发送消息:
send(aptsocket, message.UTF8String, strlen(message.UTF8String), 0);
之一个参数:accept返回的标号理解为当前socket;
第二个参数:发送的消息字符char *型数据;
第三个参数:一般设置0;
返回值:发送的数据长度。
服务端代码:
/*
ipv6
struct sockaddr_in6 addr6 = {0};
bzero(&addr6, sizeof(addr6));
addr6.sin6_len = sizeof(addr6);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(connect_port);
htons将主机的无符号短整形数转换成网络字节顺序
htonl将主机的无符号长整形数转换成网络字节顺序
*/
#import “ViewController.h”
#import
#import //inet_addr
#define connect_host @”127.0.0.1″
#define connect_port 6666
@interface ViewController ()
{
int sockaddr;//创建的socket地址
int aptsocket;//同意后返回的socket地址
dispatch_queue_t queSerial;//接收消息的队列
BOOL socket_flag;//socket标识
}
@end
@implementation ViewController
– (void)viewDidLoad {
;
//button
UIButton *button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 100, 200, 30);
button.backgroundColor = ;
;
button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 150, 200, 30);
button.backgroundColor = ;
;
button = ;
button.frame = CGRectMake((self.view.frame.size.width-200)/2, 200, 200, 30);
button.backgroundColor = ;
;
//创建服务端socket
;
}
-(void)btn:(UIButton *)button{
if (
;
}else if (
;
}else if (
;
}
}
//初始化socket
-(BOOL)initServerSocket{
socket_flag = YES;
//1、创建一个socket
sockaddr = socket(AF_INET, SOCK_STREAM, 0);
NSLog(@”sockaddr:%d”,sockaddr);
if (sockaddr==-1) {
NSLog(@”创建失败”);
return NO;
}
//ipv4
struct sockaddr_in addr4 = {0};
//参数说明:s 要置零的数据的起始地址; n 要置零的数据字节个数。
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(connect_port);//将主机的无符号短整形数转换成网络字节顺序
addr4.sin_addr.s_addr = INADDR_ANY;//就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”
//2、端口绑定
int error = bind(sockaddr, (const struct sockaddr *)&addr4, sizeof(addr4));
if (error!=0) {
NSLog(@”绑定失败”);
return NO;
}
//3、监听端口
error = listen(sockaddr, 5);//开始监听第二个参数可以排队的更大连接个数
if (error!=0) {
NSLog(@”监听失败”);
return NO;
}
//4、轮询
if (!queSerial) {
queSerial = dispatch_queue_create(“receive_queue”, DISPATCH_QUEUE_SERIAL);
}
dispatch_async(queSerial, ^{
;
});
return YES;
}
//轮询接收消息
-(void)receiveMessage{
while (true) {
NSLog(@”currentThread:%@”,);
struct sockaddr_in aptsockaddr;
基于JAVA socket的服务器客户端编程
要完成这个工作,需要完成三个部分的工作,以下依次说明:
一、建立服务器类
Java中有一个专门用来建立Socket服务器的类,名叫ServerSocket,可以用服务器需要使用的端口号作为参数来创建服务器对象。
ServerSocket server = new ServerSocket(9998)
这条语句创建了一个服务器对象,这个服务器使用9998号端口即在端口9998上注册服务,这里稍微要注意的是端口的分配必须是唯一的。因为端口是为了唯一标识每台计算机唯一服务的,另外端口号是从0~65535之间的,前1024个端口已经被Tcp/Ip 作为保轿吵留端口,因此你所分配的端口只能是1024个之后的。当一个客户端程序建立一个Socket连接,所连接的端口号为9998时,服务器对象server便响应这个连接,并且server.accept()方法会创建一个Socket对象。服务器端便可以利用这个Socket对象与客户进行通讯。
Socket incoming = server.accept() ; // 监听窗口,等待连接
进而得到输入流和输出流,并进行封装
BufferedReader in = new BufferedReader(new
InputStreamReader(incoming.getInputStream()));
/*
当读取文件时,先把内容读到缓存中,当调用in.readLine()时,再从缓存中以字符的方式读取数据(以下简称“缓存字节读取方式”)。
*/
PrintWriter ut = new PrintWriter(incoming.getOutputStream(),true);
随后,就可以使用in.readLine()方法得到客户端的输入,也可以使用out.println()方法向客户端发送数据。从而可以根据程序的需要对客户端的不同请求进行回应。
在所有通讯结束以后应该关闭这两个数据流,关闭的顺序是先关闭输出流,再关闭输入流,即使用
out.close();
in.close();
二、建立客户端代码
相桥掘比服务器端,客户端要闭消侍简单一些,客户端只需用服务器所在机器的ip以及服务器的端口作为参数创建一个Socket对象。得到这个对象后,就可以用”建立服务器”部分介绍的方法实现数据的输入和输出。
Socket socket = new Socket(“168.160.12.42”,9998);
或:
Socket socket = new Socket(InetAddress.getLocalHost(),5678);// 向主机名为InetAddress.getLocalHost()的服务器申请连接
客户机必须知道有关服务器的IP地址,对于着一点Java也提供了一个相关的类InetAddress 该对象的实例必须通过它的静态方法来提供,它的静态方法主要提供了得到本机IP 和通过名字或IP直接得到InetAddress的方法。
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(),true);
以上的程序代码建立了一个Socket对象,这个对象连接到ip地址为168.160.12.42的主机上、端口为9998的服务器对象。并且建立了输入流和输出流,分别对应服务器的输出和客户端的写入。
三、实例分析
服务方:
import java.io.*;
import java.net.*;
public class MyServer {
public static void main(String args) throws IOException{
ServerSocket server=new ServerSocket(5678); //在端口5678上注册服务
Socket client=server.accept();// 监听窗口,等待连接
BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
BufferedReader serverInput=new BufferedReader(new InputStreamReader(System.in));
PrintWriter ut=new PrintWriter(client.getOutputStream());
while(true){
String str=in.readLine();//// 读取从client传来的数据信息
str = serverInput.readLine(); // 读取用户键盘输入的字符串
System.out.println(str);//服务器控制台输出数据信息
out.println(“has receive….”); //服务器向客户端发送信息:has receive….
out.flush();
if(str.equals(“end”))
break;
}
client.close();
}
}
这个程序的主要目的在于服务器不断接收客户机所写入的信息只到,客户机发送”End”字符串就退出程序,并且服务器也会做出”Receive”为回应,告知客户机已接收到消息。
客户机代码:
import java.net.*;
import java.io.*;
public class Client{
static Socket server;
public static void main(String args)throws Exception{
server=new Socket(InetAddress.getLocalHost(),5678);// 向主机名为InetAddress.getLocalHost()的服务器申请连接
BufferedReader in=new BufferedReader(new InputStreamReader(server.getInputStream())); //客户端建立输入流并进行封装
PrintWriter ut=new PrintWriter(server.getOutputStream());
BufferedReader wt=new BufferedReader(new InputStreamReader(System.in)); //客户端从键盘输入信息
while(true){
String str=wt.readLine(); //客户端读取(获得)键盘的字符串
String str1=in.readLine(); // 从服务器获得字符串
out.println(str);//客户端向服务器发送信息
out.flush();
if(str.equals(“end”)){
break;
}
System.out.println(in.readLine());
}
server.close();
}
}
客户机代码则是接受客户键盘输入,并把该信息输出,然后输出”End”用来做退出标识。
这个程序只是简单的两台计算机之间的通讯,如果是多个客户同时访问一个服务器呢?你可以试着再运行一个客户端,结果是会抛出异常的。那么多个客户端如何实现呢?
其实,简单的分析一下,就可以看出客户和服务通讯的主要通道就是Socket本身,而服务器通过accept方法就是同意和客户建立通讯.这样当客户建立Socket的同时。服务器也会使用这一根连线来先后通讯,那么既然如此只要我们存在多条连线就可以了。那么我们的程序可以变为如下:
服务器:
import java.io.*;
import java.net.*;
public class MyServer {
public static void main(String args) throws IOException{
ServerSocket server=new ServerSocket(5678);
while(true){
Socket client=server.accept();
BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter ut=new PrintWriter(client.getOutputStream());
while(true){
String str=in.readLine();
System.out.println(str);
out.println(“has receive….”);
out.flush();
if(str.equals(“end”))
break;
}
client.close();
}
}
}
这里仅仅只是加了一个外层的While循环,这个循环的目的就是当一个客户进来就为它分配一个Socket直到这个客户完成一次和服务器的交互,这里也就是接受到客户的”End”消息.那么现在就实现了多客户之间的交互了。但是.问题又来了,这样做虽然解决了多客户,可是是排队执行的。也就是说当一个客户和服务器完成一次通讯之后下一个客户才可以进来和服务器交互,无法做到同时服务,那么要如何才能同时达到既能相互之间交流又能同时交流呢?很显然这是一个并行执行的问题了。所以线程是更好的解决方案。
那么下面的问题是如何使用线程.首先要做的事情是创建线程并使得其可以和网络连线取得联系。然后由线程来执行刚才的操作,要创建线程要么直接继承Thread要么实现Runnable接口,要建立和Socket的联系只要传递引用就可以了.而要执行线程就必须重写run方法,而run方法所做的事情就是刚才单线程版本main所做的事情,因此我们的程序变成了这样:
import java.net.*;
import java.io.*;
public class MultiUser extends Thread{
private Socket client;
public MultiUser(Socket c){
this.client=c;
}
public void run(){
try{
BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter ut=new PrintWriter(client.getOutputStream());
//Mutil User but can’t parallel
while(true){
String str=in.readLine();
System.out.println(str);
out.println(“has receive….”);
out.flush();
if(str.equals(“end”))
break;
}
client.close();
}catch(IOException ex){
}finally{
}
}
public static void main(String args)throws IOException{
ServerSocket server=new ServerSocket(5678);
while(true){
//transfer location change Single User or Multi User
MultiUser mu=new MultiUser(server.accept());
mu.start();
}
}
}
我的类直接从Thread类继承了下来.并且通过构造函数传递引用和客户Socket建立了联系,这样每个线程就有了。一个通讯管道.同样我们可以填写run方法,把之前的操作交给线程来完成,这样多客户并行的Socket就建立起来了。
关于服务器与客户端建立socket过程的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。