Reputation: 907
Does anyone know of a simple TCP example for iOS devices so I can send a string to a server. I've had a look at the following library https://github.com/robbiehanson/CocoaAsyncSocket but it seems very verbose.
All I really want is to have a simple way of connecting to an IP address & port number and sending a string of data to this address. Does anyone know of a simple way to do this?
Upvotes: 16
Views: 57089
Reputation: 1331
Sample from Apple shows how to use various Apple APIs to run a TCP connection.
I had try these method to use socket on Mac.
Upvotes: 0
Reputation: 153
import Network
@objc protocol TCPConnectionDelegate {
@objc optional func deviceStatusUpdated(isUpdated:Bool)
@objc optional func deviceSwitchToNetwork(deviceId:String)
@objc optional func TcpConnectionState(state:String,ip:String)
}
class TCPConnection:NSObject{
var delegate:TCPConnectionDelegate?
var connection: NWConnection?
final func start(host:NWEndpoint.Host) {
connection = NWConnection(host: host, port: 1234, using: .tcp)
connection!.stateUpdateHandler = self.stateDidChange(to:)
self.setupReceive(on: connection!)
connection!.start(queue: .main)
//self.connection = connection
}
func stateDidChange(to state: NWConnection.State) {
var ipAddressWithPort = connection!.endpoint.debugDescription
let ip = ipAddressWithPort.components(separatedBy: ":")
switch state {
case .setup:
break
case .waiting(let error):
print("Errrooor",error)
self.delegate?.TcpConnectionState!(state: error.localizedDescription, ip: ip[0])
//self.connectionDidFail(error: error)
case .preparing:
break
case .ready:
print("Readddy",connection?.endpoint.debugDescription)
print("IPAADERESS",ip[0])
self.delegate?.TcpConnectionState!(state: "Connected",ip: ip[0])
case .failed(let error):
print("FAiled",error)
self.delegate?.TcpConnectionState!(state: error.localizedDescription, ip: ip[0])
case .cancelled:
break
}
}
func setupReceive(on connection: NWConnection) {
connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { (data, contentContext, isComplete, error) in
if let data = data, !data.isEmpty {
print("Received:", String(data: data, encoding: .utf8) )
let stringData = String(data: data, encoding: .utf8)
let data = stringData!.data(using: .utf8)
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String:Any]
print("REsponseeeeData",json)
}catch{
}
}
if isComplete {
self.connectionDidEnd()
} else if let error = error {
self.connectionDidFail(error: error)
} else {
self.setupReceive(on: connection)
}
}
}
var didStopCallback: ((Error?) -> Void)? = nil
private func connectionDidFail(error: Error) {
print("connection did fail, error: \(error)")
stop(error: error)
}
private func connectionDidEnd() {
print("connection did end")
stop(error: nil)
}
private func stop(error: Error?) {
connection!.stateUpdateHandler = nil
connection!.cancel()
if let didStopCallback = didStopCallback {
self.didStopCallback = nil
didStopCallback(error)
}
}
func sendStreamOriented(connection: NWConnection, data: Data) {
connection.send(content: data, completion: .contentProcessed({ error in
if let error = error {
print("Errrorrrr",error)
// self.connectionDidFail(error: error)
}
}))
}
func sendEndOfStream(connection: NWConnection) {
connection.send(content: nil, contentContext: .defaultStream, isComplete: true, completion: .contentProcessed({ error in
if let error = error {
print("Errrorrrr11111",error)
// self.connectionDidFail(error: error)
}
}))
}
func sendMsg(message: [String:Any]) {
let json = try? JSONSerialization.data(withJSONObject: message, options: .prettyPrinted)
guard let jsnStr = String(data: json!, encoding: .utf8) else { return }
print("ekdhkhfkehflhf",jsnStr)
let msg = jsnStr + "\r\n"
let data: Data? = msg.data(using: .utf8)
connection!.send(content: data, completion: .contentProcessed { (sendError) in
if let sendError = sendError {
print("\(sendError)")
}
})
self.setupReceive(on: connection!)
}
func cancel() {
connection!.cancel()
}
}
Upvotes: 0
Reputation: 7718
@Mohamad Chami's example was really great, tried to write a Swift version of it
GitHubLink
class SocketDataManager: NSObject, StreamDelegate {
var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?
var inputStream: InputStream?
var outputStream: OutputStream?
var messages = [AnyHashable]()
weak var uiPresenter :PresenterProtocol!
init(with presenter:PresenterProtocol){
self.uiPresenter = presenter
}
func connectWith(socket: DataSocket) {
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (socket.ipAddress! as CFString), UInt32(socket.port), &readStream, &writeStream)
messages = [AnyHashable]()
open()
}
func disconnect(){
close()
}
func open() {
print("Opening streams.")
outputStream = writeStream?.takeRetainedValue()
inputStream = readStream?.takeRetainedValue()
outputStream?.delegate = self
inputStream?.delegate = self
outputStream?.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
inputStream?.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode)
outputStream?.open()
inputStream?.open()
}
func close() {
print("Closing streams.")
uiPresenter?.resetUIWithConnection(status: false)
inputStream?.close()
outputStream?.close()
inputStream?.remove(from: RunLoop.current, forMode: .defaultRunLoopMode)
outputStream?.remove(from: RunLoop.current, forMode: .defaultRunLoopMode)
inputStream?.delegate = nil
outputStream?.delegate = nil
inputStream = nil
outputStream = nil
}
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
print("stream event \(eventCode)")
switch eventCode {
case .openCompleted:
uiPresenter?.resetUIWithConnection(status: true)
print("Stream opened")
case .hasBytesAvailable:
if aStream == inputStream {
var dataBuffer = Array<UInt8>(repeating: 0, count: 1024)
var len: Int
while (inputStream?.hasBytesAvailable)! {
len = (inputStream?.read(&dataBuffer, maxLength: 1024))!
if len > 0 {
let output = String(bytes: dataBuffer, encoding: .ascii)
if nil != output {
print("server said: \(output ?? "")")
messageReceived(message: output!)
}
}
}
}
case .hasSpaceAvailable:
print("Stream has space available now")
case .errorOccurred:
print("\(aStream.streamError?.localizedDescription ?? "")")
case .endEncountered:
aStream.close()
aStream.remove(from: RunLoop.current, forMode: .defaultRunLoopMode)
print("close stream")
uiPresenter?.resetUIWithConnection(status: false)
default:
print("Unknown event")
}
}
func messageReceived(message: String){
uiPresenter?.update(message: "server said: \(message)")
print(message)
}
func send(message: String){
let response = "msg:\(message)"
let buff = [UInt8](message.utf8)
if let _ = response.data(using: .ascii) {
outputStream?.write(buff, maxLength: buff.count)
}
}
}
class ViewController: UIViewController {
var socketConnector:SocketDataManager!
@IBOutlet weak var ipAddressField: UITextField!
@IBOutlet weak var portField: UITextField!
@IBOutlet weak var messageField: UITextField!
@IBOutlet weak var messageHistoryView: UITextView!
@IBOutlet weak var connectBtn: UIButton!
@IBOutlet weak var sendBtn: UIButton!
@IBOutlet weak var statusView: UIView!
@IBOutlet weak var statusLabl: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
socketConnector = SocketDataManager(with: self)
resetUIWithConnection(status: false)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBAction func connect(){
//http://localhost:50694/
guard let ipAddr = ipAddressField.text, let portVal = portField.text else {
return
}
let soc = DataSocket(ip: ipAddr, port: portVal)
socketConnector.connectWith(socket: soc)
}
@IBAction func send(){
guard let msg = messageField.text else {
return
}
send(message: msg)
messageField.text = ""
}
func send(message: String){
socketConnector.send(message: message)
update(message: "me:\(message)")
}
}
extension ViewController: PresenterProtocol{
func resetUIWithConnection(status: Bool){
ipAddressField.isEnabled = !status
portField.isEnabled = !status
messageField.isEnabled = status
connectBtn.isEnabled = !status
sendBtn.isEnabled = status
if (status){
updateStatusViewWith(status: "Connected")
}else{
updateStatusViewWith(status: "Disconnected")
}
}
func updateStatusViewWith(status: String){
statusLabl.text = status
}
func update(message: String){
if let text = messageHistoryView.text{
let newText = """
\(text)
\(message)
"""
messageHistoryView.text = newText
}else{
let newText = """
\(message)
"""
messageHistoryView.text = newText
}
let myRange=NSMakeRange(messageHistoryView.text.count-1, 0);
messageHistoryView.scrollRangeToVisible(myRange)
}
}
struct DataSocket {
let ipAddress: String!
let port: Int!
init(ip: String, port: String){
self.ipAddress = ip
self.port = Int(port)
}
}
Upvotes: 12
Reputation: 858
Those who are unable to receive data at the server side:
It is perhaps due to data encoding mechanism. @Mohamad Chami's answer works fine after changing the data encoding mechanism at the sendMessage
method as follows:
In his example, NSString
is converted to NSData
by:
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
Change the NSASCIIStringEncoding
to NSUTF8StringEncoding
. It is because at server's side (Oracle database server in my case), the data is received in modified UTF-8 encoding.
Upvotes: 3
Reputation: 534
https://github.com/swiftsocket/SwiftSocket Swift Socket library provide you a simple interface for socket based connection. Refer to this link and below sample.
let client = TCPClient(address: "www.apple.com", port: 80)
switch client.connect(timeout: 1) {
case .success:
switch client.send(string: "GET / HTTP/1.0\n\n" ) {
case .success:
guard let data = client.read(1024*10) else { return }
if let response = String(bytes: data, encoding: .utf8) {
print(response)
}
case .failure(let error):
print(error)
}
case .failure(let error):
print(error)
}
Upvotes: 3
Reputation: 1234
SocketConnectionVC.h
#import <UIKit/UIKit.h>
@interface SocketConnectionVC : UIViewController<NSStreamDelegate>
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
NSInputStream *inputStream;
NSOutputStream *outputStream;
NSMutableArray *messages;
}
@property (weak, nonatomic) IBOutlet UITextField *ipAddressText;
@property (weak, nonatomic) IBOutlet UITextField *portText;
@property (weak, nonatomic) IBOutlet UITextField *dataToSendText;
@property (weak, nonatomic) IBOutlet UITextView *dataRecievedTextView;
@property (weak, nonatomic) IBOutlet UILabel *connectedLabel;
@end
SocketConnectionVC.m
#import "SocketConnectionVC.h"
@interface SocketConnectionVC ()
@end
@implementation SocketConnectionVC
- (void)viewDidLoad {
[super viewDidLoad];
_connectedLabel.text = @"Disconnected";
}
- (IBAction) sendMessage {
NSString *response = [NSString stringWithFormat:@"msg:%@", _dataToSendText.text];
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
- (void) messageReceived:(NSString *)message {
[messages addObject:message];
_dataRecievedTextView.text = message;
NSLog(@"%@", message);
}
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(@"stream event %lu", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(@"Stream opened");
_connectedLabel.text = @"Connected";
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream)
{
uint8_t buffer[1024];
NSInteger len;
while ([inputStream hasBytesAvailable])
{
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output)
{
NSLog(@"server said: %@", output);
[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(@"Stream has space available now");
break;
case NSStreamEventErrorOccurred:
NSLog(@"%@",[theStream streamError].localizedDescription);
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
_connectedLabel.text = @"Disconnected";
NSLog(@"close stream");
break;
default:
NSLog(@"Unknown event");
}
}
- (IBAction)connectToServer:(id)sender {
NSLog(@"Setting up connection to %@ : %i", _ipAddressText.text, [_portText.text intValue]);
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef) _ipAddressText.text, [_portText.text intValue], &readStream, &writeStream);
messages = [[NSMutableArray alloc] init];
[self open];
}
- (IBAction)disconnect:(id)sender {
[self close];
}
- (void)open {
NSLog(@"Opening streams.");
outputStream = (__bridge NSOutputStream *)writeStream;
inputStream = (__bridge NSInputStream *)readStream;
[outputStream setDelegate:self];
[inputStream setDelegate:self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream open];
[inputStream open];
_connectedLabel.text = @"Connected";
}
- (void)close {
NSLog(@"Closing streams.");
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream setDelegate:nil];
[outputStream setDelegate:nil];
inputStream = nil;
outputStream = nil;
_connectedLabel.text = @"Disconnected";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
snapshot for ui of this SocketConnectionVC
and Follow these steps
1- input the ip on ipAdress textfield
2- input the port on port textfield
3- press connect button
4- (make sure the ip address and port is correct and the open of stream is fine. you can show the status of stream on console of Xcode)
5- input data to send to server
6- press send button
7- you can show the received message from server on the text view above connect button
Upvotes: 28
Reputation: 49
Maybe try here: http://www.raywenderlich.com/3932/how-to-create-a-socket-based-iphone-app-and-server
Ray gives nice example of building custom server using Python + iOS client application. He has very nice set of tutorials on iOS topics - it is worth to visit his site.
Upvotes: 2